同為縱向滑動(dòng)沖突(核心代碼)
- 思路:根據(jù)業(yè)務(wù)邏輯處理,使用外部攔截
- 首先有個(gè)父容器StickyLayout繼承LinearLayout,內(nèi)部放header和一個(gè)Listview;
- 滑動(dòng)規(guī)則:攔截:Header顯示;listview滑動(dòng)到頂部椿每;Header隱藏骨杂,并且listview滑動(dòng)手勢(shì)為向下滑動(dòng);其他情況不攔截裸扶。
- 先看下布局:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.ceshiactivity.viewdemo.DemoCoreActivity">
<com.example.ceshiactivity.viewdemo.StickyLayout
android:id="@+id/sticky_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ffffff"
android:orientation="vertical">
<LinearLayout
android:id="@+id/sticky_header"
android:layout_width="match_parent"
android:layout_height="100dp"
android:background="#78A524"
android:gravity="center"
android:orientation="vertical">
<ImageView
android:id="@+id/imageView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/ic_launcher" />
</LinearLayout>
<LinearLayout
android:id="@+id/sticky_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ListView
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#fff4f7f9"
android:cacheColorHint="#00000000"
android:divider="#dddbdb"
android:dividerHeight="1.0px"
android:listSelector="@android:color/transparent" />
</LinearLayout>
</com.example.ceshiactivity.viewdemo.StickyLayout>
</RelativeLayout>
DemoCoreActivity
這里主要調(diào)用一個(gè)接口调窍,以獲得listview的item0位置
public class DemoCoreActivity extends AppCompatActivity implements StickyLayout.OnGiveUpTouchEventListener {
private StickyLayout stickyLayout;
ListView listView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_demo_core);
initView();
LinearLayout ll= (LinearLayout) findViewById(R.id.sticky_header);
stickyLayout= (StickyLayout) findViewById(R.id.sticky_layout);
stickyLayout.setOnGiveUpTouchEventListener(this);
}
private void initView() {
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);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
Toast.makeText(DemoCoreActivity.this, "click item",
Toast.LENGTH_SHORT).show();
}
});
}
@Override
public boolean giveUpTouchEvent(MotionEvent event) {
//如果位置為0,獲取0位置的view
if (listView.getFirstVisiblePosition() == 0) {
View view = listView.getChildAt(0);
//view在頂部位置單位像素
if (view != null && view.getTop() >= 0) {
return true;
}
}
return false;
}
然后是核心代碼StickyLayout
public class StickyLayout extends LinearLayout {
private static final String TAG = "StickyLayout";
private static final boolean DEBUG = true;
//用來(lái)判斷l(xiāng)istview是否滑動(dòng)到頂端
public interface OnGiveUpTouchEventListener {
public boolean giveUpTouchEvent(MotionEvent event);
}
private View mHeader;//上半部分布局
private View mContent;//下半部分布局
private OnGiveUpTouchEventListener mGiveUpTouchEventListener;
// header的高度 單位:px
private int mOriginalHeaderHeight;
private int mHeaderHeight;
//滑動(dòng)方向
private int mStatus = STATUS_EXPANDED;
public static final int STATUS_EXPANDED = 1;
public static final int STATUS_COLLAPSED = 2;
//滾動(dòng)的像素值
private int mTouchSlop;
// 分別記錄上次滑動(dòng)的坐標(biāo)
private int mLastX = 0;
private int mLastY = 0;
// 分別記錄上次滑動(dòng)的坐標(biāo)(onInterceptTouchEvent)
private int mLastXIntercept = 0;
private int mLastYIntercept = 0;
// 用來(lái)控制滑動(dòng)角度旧蛾,僅當(dāng)角度a滿足如下條件才進(jìn)行滑動(dòng):tan a = deltaX / deltaY > 2
private static final int TAN = 2;
private boolean mIsSticky = true;
private boolean mInitDataSucceed = false;
private boolean mDisallowInterceptTouchEventOnHeader = true;
public StickyLayout(Context context) {
super(context);
}
public StickyLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public StickyLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
public void onWindowFocusChanged(boolean hasWindowFocus) {
super.onWindowFocusChanged(hasWindowFocus);
if (hasWindowFocus && (mHeader == null || mContent == null)) {
initData();
}
}
private void initData() {
int headerId= getResources().getIdentifier("sticky_header", "id", getContext().getPackageName());
int contentId = getResources().getIdentifier("sticky_content", "id", getContext().getPackageName());
if (headerId != 0 && contentId != 0) {
mHeader = findViewById(headerId);
mContent = findViewById(contentId);
mOriginalHeaderHeight = mHeader.getMeasuredHeight();
mHeaderHeight = mOriginalHeaderHeight;
mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
if (mHeaderHeight > 0) {
mInitDataSucceed = true;
}
if (DEBUG) {
Log.d(TAG, "mTouchSlop = " + mTouchSlop + "mHeaderHeight = " + mHeaderHeight);
}
} else {
throw new NoSuchElementException("Did your view with id \"sticky_header\" or \"sticky_content\" exists?");
}
}
/**
* 傳遞跟布局
* @param l this
*/
public void setOnGiveUpTouchEventListener(OnGiveUpTouchEventListener l) {
mGiveUpTouchEventListener = l;
}
/**
* 片段
*/
@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()) {
//豎直滑動(dòng)距離小于header高度,不攔截
intercepted = 0;
} else if (Math.abs(deltaY) <= Math.abs(deltaX)) {
//豎直距離差小于等于水平距離不攔截
intercepted = 0;
} else if (mStatus == STATUS_EXPANDED && deltaY <= -mTouchSlop) {
//header展開狀態(tài)蠕嫁,向上滑動(dòng)锨天,攔截
intercepted = 1;
} else if (mGiveUpTouchEventListener != null) {
//布局不為空
if (mGiveUpTouchEventListener.giveUpTouchEvent(event) && deltaY >= mTouchSlop) {
//listview滑動(dòng)到頂部并向下滑動(dòng),攔截
intercepted = 1;
}
}
break;
}
case MotionEvent.ACTION_UP: {
intercepted = 0;
mLastXIntercept = mLastYIntercept = 0;
break;
}
default:
break;
}
if (DEBUG) {
Log.d(TAG, "intercepted=" + intercepted);
}
return intercepted != 0 && mIsSticky;
}
/**
* 片段
*/
@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;
if (DEBUG) {
Log.d(TAG, "mHeaderHeight=" + mHeaderHeight + " deltaY=" + deltaY + " mlastY=" + mLastY);
}
mHeaderHeight += deltaY;
setHeaderHeight(mHeaderHeight);
break;
}
case MotionEvent.ACTION_UP: {
// 這里做了下判斷剃毒,當(dāng)松開手的時(shí)候病袄,會(huì)自動(dòng)向兩邊滑動(dòng),具體向哪邊滑赘阀,要看當(dāng)前所處的位置
int destHeight = 0;
if (mHeaderHeight <= mOriginalHeaderHeight * 0.5) {
destHeight = 0;
mStatus = STATUS_COLLAPSED;
} else {
destHeight = mOriginalHeaderHeight;
mStatus = STATUS_EXPANDED;
}
// 慢慢滑向終點(diǎn)
this.smoothSetHeaderHeight(mHeaderHeight, destHeight, 500);
break;
}
default:
break;
}
mLastX = x;
mLastY = y;
return true;
}
public void smoothSetHeaderHeight(final int from, final int to, long duration) {
smoothSetHeaderHeight(from, to, duration, false);
}
/**
* 線程設(shè)置高度變化
* @param from head初始高度
* @param to 移動(dòng)到的高度
* @param duration 時(shí)間
* @param modifyOriginalHeaderHeight
*/
public void smoothSetHeaderHeight(final int from, final int to, long duration, final boolean modifyOriginalHeaderHeight) {
final int frameCount = (int) (duration / 1000f * 30) + 1;
//速度公式
final float partation = (to - from) / (float) frameCount;
new Thread("Thread#smoothSetHeaderHeight") {
@Override
public void run() {
for (int i = 0; i < frameCount; i++) {
//動(dòng)態(tài)變化
final int height;
if (i == frameCount - 1) {
height = to;
} else {
height = (int) (from + partation * i);
}
post(new Runnable() {
public void run() {
setHeaderHeight(height);
}
});
try {
sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (modifyOriginalHeaderHeight) {
setOriginalHeaderHeight(to);
}
};
}.start();
}
public void setOriginalHeaderHeight(int originalHeaderHeight) {
mOriginalHeaderHeight = originalHeaderHeight;
}
public void setHeaderHeight(int height, boolean modifyOriginalHeaderHeight) {
if (modifyOriginalHeaderHeight) {
setOriginalHeaderHeight(height);
}
setHeaderHeight(height);
}
/**
* 賦值 并設(shè)置高度到Header中
* @param height
*/
public void setHeaderHeight(int height) {
if (!mInitDataSucceed) {
initData();
}
if (DEBUG) {
Log.d(TAG, "setHeaderHeight height=" + height);
}
if (height <= 0) {
height = 0;
} else if (height > mOriginalHeaderHeight) {
//這行注釋掉可實(shí)現(xiàn)下拉到任意位置后彈回
height = mOriginalHeaderHeight;
}
if (height == 0) {
mStatus = STATUS_COLLAPSED;
} else {
mStatus = STATUS_EXPANDED;
}
if (mHeader != null && mHeader.getLayoutParams() != null) {
mHeader.getLayoutParams().height = height;
mHeader.requestLayout();
mHeaderHeight = height;
} else {
if (DEBUG) {
Log.e(TAG, "null LayoutParams when setHeaderHeight");
}
}
}
public int getHeaderHeight() {
return mHeaderHeight;
}
public void setSticky(boolean isSticky) {
mIsSticky = isSticky;
}
public void requestDisallowInterceptTouchEventOnHeader(boolean disallowIntercept) {
mDisallowInterceptTouchEventOnHeader = disallowIntercept;
}
}
效果圖:
縱向與縱向滑動(dòng)沖突.jpg
遺留問(wèn)題手指向上滑動(dòng)不離開屏幕則listview無(wú)法滑動(dòng)(待解決)