最近重新翻看开发艺术探索,发现内外同方向的滑动冲突案例代码不完整,所以决定补充完整。
activity_main的布局文件:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <com.example.mokunfeng.myapplication.StickyLayout android:layout_height="match_parent" android:layout_width="match_parent" android:orientation="vertical" android:id="@+id/StickyL"> <ImageView android:layout_width="match_parent" android:layout_height="wrap_content" android:scaleType="centerCrop" android:adjustViewBounds="true" android:id="@+id/head" android:src="@mipmap/help__choice_devices"/> <ListView android:layout_height="wrap_content" android:layout_width="wrap_content" android:id="@+id/list"> </ListView> </com.example.mokunfeng.myapplication.StickyLayout> </LinearLayout> MainActivity代码: public class MainActivity extends AppCompatActivity { @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) @Override protected void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); StickyLayout mstickyLayout = (StickyLayout)findViewById(R.id.StickyL); final ListView listView = findViewById(R.id.list); ArrayList<String> datas = new ArrayList<String>(); for(int i = 0; i < 50;i++){ datas.add("name"+i); } ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,R.layout.content_list_item,R.id.name,datas); listView.setAdapter(adapter); mstickyLayout.getOnGiveUpTouchEventListener(new StickyLayout.OnGiveUpTouchEventListener(){ @Override public boolean giveUpTouchEvent(MotionEvent event) { if(listView.getFirstVisiblePosition() == 0){ //listView中可见的第一个位置 View view = listView.getChildAt(0); if(view !=null && view.getTop() >= 0){ return true; } } return false; } }); } }
自定义的可上下滑动的LinearLayout
public class StickyLayout extends LinearLayout { private int mTouchSlop; private int mLastX = 0; private int mLastY =0; private int mLastXIntercept = 0; private int mLastYIntercept = 0; private int mHeaderHeight; private int mOriginalHeaderHeight; private boolean mDisallowInterceptTouchEventOnHeader = true; private boolean mIsSticky = true; private static final int STATUS_EXPANDED = 1; private static final int STATUS_COLLAPSED = 2; //向上 private int mStatus = STATUS_EXPANDED; ImageView mIvHead; ListView mLvlist; private OnGiveUpTouchEventListener mGiveUpTouchEventListener; public StickyLayout(Context context) { super(context); } public void getOnGiveUpTouchEventListener(OnGiveUpTouchEventListener giveUpTouchEventListener){ this.mGiveUpTouchEventListener =giveUpTouchEventListener; } public StickyLayout(Context context, @Nullable AttributeSet attrs) { super(context, attrs); } public StickyLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) public StickyLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } @Override public void onWindowFocusChanged(boolean hasWindowFocus) { super.onWindowFocusChanged(hasWindowFocus); if (hasWindowFocus && (mIvHead == null || mIvHead == null)) { init(); } } private void init(){ mIvHead = findViewById(R.id.head); mLvlist = findViewById(R.id.list); mOriginalHeaderHeight = mIvHead.getMeasuredHeight(); mHeaderHeight = mOriginalHeaderHeight; mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); } @Override public boolean onInterceptTouchEvent(MotionEvent event){ int intercepted = 0; int x = (int)event.getX(); int y = (int)event.getY(); switch(event.getAction()){ case MotionEvent.ACTION_DOWN: { mLastXIntercept = x; mLastYIntercept = y; mLastX = x; mLastY = y; intercepted = 0; break; } case MotionEvent.ACTION_MOVE:{ int deltaX = x - mLastXIntercept; int deltaY = y - mLastYIntercept; if(mDisallowInterceptTouchEventOnHeader && y <= getHeaderHeight()){ intercepted = 0; }else if(Math.abs(deltaY) <= Math.abs(deltaX)){ intercepted = 0; }else if(mStatus == STATUS_EXPANDED && deltaY <= -mTouchSlop){ intercepted = 1; }else if(mGiveUpTouchEventListener != null){ if(mGiveUpTouchEventListener.giveUpTouchEvent(event) && deltaY >= -mTouchSlop){ intercepted = 1; } } break; } case MotionEvent.ACTION_UP:{ intercepted = 0; mLastXIntercept = mLastYIntercept = 0; break; } default: break; } return intercepted != 0 && mIsSticky; } public int getHeaderHeight() { return mHeaderHeight; } public interface OnGiveUpTouchEventListener { boolean giveUpTouchEvent(MotionEvent event); } @Override public boolean onTouchEvent(MotionEvent event){ if(!mIsSticky){ return true; } int x = (int) event.getX(); int y = (int)event.getY(); switch(event.getAction()){ case MotionEvent.ACTION_DOWN:{ break; } case MotionEvent.ACTION_MOVE:{ int deltaX = x - mLastX; int deltaY = y - mLastY; mHeaderHeight += deltaY; setHeaderHeight(mHeaderHeight); break; } case MotionEvent.ACTION_UP:{ int destHeight = 0; if(mHeaderHeight <= mOriginalHeaderHeight *0.5){ destHeight = 0; mStatus = STATUS_COLLAPSED; }else{ destHeight = mOriginalHeaderHeight; mStatus = STATUS_EXPANDED; } this.smoothSetHeaderHeight(mHeaderHeight,destHeight,500); break; } default: break; } mLastX = x; mLastY = y; return true; } public void setHeaderHeight(int height){ if(height <= 0){ height = 0; }else if(height > mOriginalHeaderHeight){ height = mOriginalHeaderHeight; } if(mIvHead != null && mIvHead.getLayoutParams() != null){ mIvHead.getLayoutParams().height = height; mIvHead.requestLayout(); mHeaderHeight = height; } } private void smoothSetHeaderHeight(final int headerHeight, final int mdestHeight, int time ){ final int timeCount = (int) ( time/50f ); final float unitLength = (mdestHeight-headerHeight) / (float)timeCount; new Thread(){ @Override public void run(){ for(int i = 0; i< timeCount; i++){ final int height; if(i == timeCount-1){ height = mdestHeight; }else{ height = (int) (headerHeight+unitLength*i); } post(new Runnable(){ @Override public void run() { setHeaderHeight(height); } }); try { sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); } }