Android 仿網易新聞圖片新聞底部佈局

先上效果圖和網易新聞的對比html

網易新聞android

Picture

我實現的app

Picture

基本效果實現了,後面還須要進一步優化。實現的答題思路就是經過攔截滑動事件和調用layout()的實現控件的拖動和高度的限制,內容的高度是由TextViewmaxHeightminHeight來限制的。ide

初始化佈局

新建一個類繼承LinearLayout這個類就是咱們佈局容器,首先初始化這個類,須要定義一些自定義參數post

/**
     * 初始化View
     * @param context
     * @param attrs
     */
    private void initView(Context context, AttributeSet attrs) {
        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.bot_arr);

        String title = ta.getString(R.styleable.bot_arr_bot_title);
        String content = ta.getString(R.styleable.bot_arr_bot_content);
        ta.recycle();

        LayoutInflater inflater = LayoutInflater.from(context);
        mView = inflater.inflate(R.layout.bottom_layout2, this, true);

        mTitleView = (TextView) mView.findViewById(R.id.bot_title_text);
        mContentView = (TextView) mView.findViewById(R.id.bot_content);
        mPageNum = (TextView) mView.findViewById(R.id.page_num);
        mTitleLayout = (LinearLayout) mView.findViewById(R.id.title_layout);

        mOuterLayout= (LinearLayout)mView.findViewById(R.id.outer_layout);
        mContentView.setText(content);        //設置TextView能夠滾動
        mContentView.setMovementMethod(new ScrollingMovementMethod());
        mTitleView.setText(title);        this.context = context;
    }

基本都是一些很簡單的東西,可是有一句很關鍵的代碼mContentView.setMovementMethod(new ScrollingMovementMethod());,當咱們設置了TextViewmaxHeightminHeight後當字數超過了限制咱們固然但願是能夠滑動的,可是TextView是沒法滑動的。最開始個人想法是用ScrollView嵌套一個TextView可是ScrollView沒有限制高度的方法,要本身去計算最高和最低的高度很麻煩。最後在Stackoverflow上看到了解決方法,使用setMovementMethod方法,咱們看看官方文檔的解釋優化

Picture

什麼?看不懂?好吧我就是放上來裝逼的。Android中咱們爲了實現文本的滾動能夠在ScrollView中嵌入一個TextView,其實TextView本身也能夠實現多行滾動的,只須要設置一些屬性this

<TextView
            android:id="@+id/bot_content"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            //最高高度
            android:maxHeight="200dp"
            //最低高度
            android:minHeight="100dp"
            android:padding="5dp"
            //滾動反向  
            android:scrollbars="vertical"
            android:text="xxxx"
            android:textColor="#ffffff"
            android:textSize="15sp" />

固然也能夠這樣設置他的android:maxLines="2"來限制它的高度從而實現滾動。而後經過設置TextView的滾動實例也就是上面提到的方法setMovementMethod(new ScrollingMovementMethod())。這樣就實現了滾動了。spa

監聽事件code

當咱們拖動的時候,若是文字高度超過了最大的高度咱們就要整個佈局均可以上滑一點,方便用戶查看更多的文本內容。滑動佈局使用的layout()方法來調整佈局的位置。

    /**
     * 分發事件
     *
     * @param event
     * @return
     */
    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {        if (isFirst) {            // 獲得屏幕的寬
            displayMetrics = getResources().getDisplayMetrics();
            screenWidth = displayMetrics.widthPixels;            // 獲得標題欄和狀態欄的高度
            Rect rect = new Rect();
            Window window = ((Activity) context).getWindow();            int statusBarHeight = rect.top;            int contentViewTop = window.findViewById(Window.ID_ANDROID_CONTENT).getTop();            int titleBarHeight = contentViewTop - statusBarHeight;            // 獲得屏幕的高
            screenHeight = displayMetrics.heightPixels - (statusBarHeight + titleBarHeight);
            isFirst = false;


        }        if(!onTouch){            return true;
        }        switch (event.getAction()) {            case MotionEvent.ACTION_DOWN:
                lastX = event.getRawX();
                lastY = event.getRawY();                break;            case MotionEvent.ACTION_MOVE:                //移動的距離
                float distanceX = event.getRawX() - lastX;                float distanceY = event.getRawY() - lastY;                //移動後控件的座標
                left = (int) (getLeft() + distanceX);
                top = (int) (getTop() + distanceY);
                right = (int) (getRight() + distanceX);
                bottom = (int) (getBottom() + distanceY);                //處理拖出屏幕的狀況
                if (left < 0) {
                    left = 0;
                    right = getWidth();
                }                if (right > screenWidth) {
                    right = screenWidth;
                    left = screenWidth - getWidth();
                }                if (top < 0) {
                    top = 0;
                    bottom = getHeight();
                }                if (bottom > bottombot) {
                    bottom = bottombot;
                    top = bottombot - getHeight();
                }                if (bottom < maxBottom) {
                    bottom = maxBottom;
                    top = maxBottom - getHeight();
                }                //移動View
                layout(getLeft(), top, getRight(), bottom);
                lastX = event.getRawX();
                lastY = event.getRawY();                break;            case MotionEvent.ACTION_POINTER_UP:            case MotionEvent.ACTION_CANCEL:                break;
        }        return super.dispatchTouchEvent(event);
    }

MotionEvent.ACTION_DOWN時記錄用戶點擊的位置,注意getRawX()``getRawY() 是相對於屏幕位置座標,getX()``getY() 是相對於容器的位置座標。在MotionEvent.ACTION_MOVE計算用戶手指移動的距離,而後計算出容器移動後的座標,在判斷用戶移動的距離是否在容許的移動範圍內。最後經過layout()方法來移動容器。

關於getLeft(),getTop(),getRight(),getBottom()下面這張圖應該能很好的解釋清楚了

Picture

其中比較重要的兩個值是容器的最高和最低的座標位置,這兩個位置是經過內容來決定的,因此咱們在設置內容的時候須要計算獲得這兩個座標

/**
     * 設置內容
     *
     * @param content
     */
    public void setContent(final String content) {
        mIsUpdate = true;
        mContentView.setText(content);
        mTitleView.removeCallbacks(mRun);
        onTouch=false;        //添加全局佈局偵聽器
        getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {            @Override
            public void onGlobalLayout() {                if (mIsUpdate) {
                    mIsUpdate = false;                    int scHeight = mContentView.getHeight();
                    bottomtop = getTop();
                    bottomtop2=mOuterLayout.getTop();
                    bottombot = getBottom();
                    bottombot2=mOuterLayout.getBottom();

                    maxBottom = getBottom();                    if (scHeight > pixelsToDp(context,100)) {
                        bottomtop = getTop() + (mContentView.getHeight() - MINHRIGHT);
                        bottombot = getBottom() + (mContentView.getHeight() - MINHRIGHT);

                        bottomtop2 = mOuterLayout.getTop() + (mContentView.getHeight() - MINHRIGHT);
                        bottombot2 = mOuterLayout.getBottom() + (mContentView.getHeight() - MINHRIGHT);
                    }
                    mOuterLayout.layout(getLeft(), bottomtop2, getRight(), bottombot2);

                    mTitleView.postDelayed(mRun =new Runnable() {                        @Override
                        public void run() {
                            layout(getLeft(), bottomtop, getRight(), bottombot);
                            mOuterLayout.layout(getLeft(), 0, getRight(), mOuterLayout.getHeight());
                            onTouch=true;
                        }
                    }, 800);
                }
            }
        });

    }

Android中須要拿到控件的寬高或者getTop()須要在佈局文件加載完成後才能拿到,因此這裏添加了佈局監聽器,當佈局完成後回調此接口拿到咱們須要的值。

ViewTreeObserver中有許多對佈局監聽的方法

  • ViewTreeObserver.OnDrawListener 當要繪製視圖樹時調用

  • ViewTreeObserver.OnGlobalFocusChangeListener 當視圖樹中的焦點狀態改變時要調用

  • ViewTreeObserver.OnGlobalLayoutListener 當全局佈局狀態或視圖樹中的視圖的可見性更改時要調用

  • ViewTreeObserver.OnScrollChangedListener 在視圖樹中的某些內容已滾動時調用

  • ViewTreeObserver.OnWindowAttachListener 當視圖層次結構附加到其窗口並從其窗口拆離時要調用

還有不少能夠去官方文檔查看 ViewTreeObserver

而後經過內容的高度來計算拖動時getBottom()的最小值,也就是容器最高能夠拖動哪一個位置,當scHeight > pixelsToDp(context,100)也就是內容高度大於了最大的限定高度,那麼就須要從新計算容器的位置來隱藏多餘的內容

Picture

請原諒一個靈魂畫手。。。。

中間的粗黑線是當前容器應該在的位置,那麼咱們就須要把當前容器像下移必定位置隱藏部份內容, 在隱藏黑框前獲取它的getBottom()也就是黑框的最高能夠滑動的位置

Picture

紅框爲父容器,黑色的爲當前容器,能夠看到隱藏實際就是把當前容器的位置下移,紅線就是上面黑線的位置。 當前的位置經過bottombot = getBottom() + (mContentView.getHeight() - MINHRIGHT);計算獲得它當前的getBottom()的位置,bottombot也是當前容器能夠滑動的最低位置了。而後在經過bottomtop = getTop() + (mContentView.getHeight() - MINHRIGHT);計算獲得了它的getTop()位置。因爲咱們設置了內容的TextView的最大高度,因此mContentView.getHeight()獲取到的最大高度若是超過了TextViewmaxHeight就是一個固定的值,這也就固定了容器的最高滑動高度,而後經過mOuterLayout.layout(getLeft(), bottomtop2, getRight(), bottombot2);來隱藏了部份內容,

這裏還有個小問題,當換內容時容器須要計算內容的高度, 那麼就會去從新計算內容控件的位置,若是當即去設置當前容器的位置會出現閃屏的bug 經過postDelayed來延遲了隱藏當前容器多餘內容,而後在最外層又添加了一個空的佈局,這樣閃屏用戶就看不到了。 雖然解決了問題可是確定還有更好的解決辦法, 各位有什麼好的方法歡迎私信交流!

使用

使用就很簡單了,放在你須要的佈局裏面就好了,設置他的內容和標題

<com.qinanyu.bottomlayout.TestBottomLayout
        android:id="@+id/bottom_layout"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:bot_content="wtf"
        app:bot_title="江西農名工帶頭">
    </com.qinanyu.bottomlayout.TestBottomLayout>

也能夠在代碼中設置內容和標題

mBottomLayout.setTtitle("這是一個普通話新聞個普通話新聞個普通話新聞的標題");
mBottomLayout.setContent("zheshi yi shh hs卡號升到發貨發是一個普通話新聞" +                "個普通話新聞個普通話是一個普通話新聞個" +                "徽我是安個普通話是一個普通話新 ");

END ~~

原文連接:http://www.apkbus.com/blog-953084-77594.html

做者:波斯汪 連接:https://www.imooc.com/article/68653 來源:慕課網

相關文章
相關標籤/搜索