先上效果圖和網易新聞的對比html
網易新聞android
我實現的app
基本效果實現了,後面還須要進一步優化。實現的答題思路就是經過攔截滑動事件和調用layout()
的實現控件的拖動和高度的限制,內容的高度是由TextView
的maxHeight
和minHeight
來限制的。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());
,當咱們設置了TextView
的maxHeight
和minHeight
後當字數超過了限制咱們固然但願是能夠滑動的,可是TextView
是沒法滑動的。最開始個人想法是用ScrollView
嵌套一個TextView
可是ScrollView
沒有限制高度的方法,要本身去計算最高和最低的高度很麻煩。最後在Stackoverflow上看到了解決方法,使用setMovementMethod
方法,咱們看看官方文檔的解釋優化
什麼?看不懂?好吧我就是放上來裝逼的。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()
下面這張圖應該能很好的解釋清楚了
其中比較重要的兩個值是容器的最高和最低的座標位置,這兩個位置是經過內容來決定的,因此咱們在設置內容的時候須要計算獲得這兩個座標
/** * 設置內容 * * @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)
也就是內容高度大於了最大的限定高度,那麼就須要從新計算容器的位置來隱藏多餘的內容
請原諒一個靈魂畫手。。。。
中間的粗黑線是當前容器應該在的位置,那麼咱們就須要把當前容器像下移必定位置隱藏部份內容, 在隱藏黑框前獲取它的getBottom()
也就是黑框的最高能夠滑動的位置
紅框爲父容器,黑色的爲當前容器,能夠看到隱藏實際就是把當前容器的位置下移,紅線就是上面黑線的位置。 當前的位置經過bottombot = getBottom() + (mContentView.getHeight() - MINHRIGHT);
計算獲得它當前的getBottom()
的位置,bottombot
也是當前容器能夠滑動的最低位置了。而後在經過bottomtop = getTop() + (mContentView.getHeight() - MINHRIGHT);
計算獲得了它的getTop()
位置。因爲咱們設置了內容的TextView
的最大高度,因此mContentView.getHeight()
獲取到的最大高度若是超過了TextView
的maxHeight
就是一個固定的值,這也就固定了容器的最高滑動高度,而後經過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 來源:慕課網