1、设计思路
在Scroller的应用--滑屏实现中使用Scroller实现滑屏效果,这里使用Scroller与ListView实现相似QQ滑动。然后点击删除功能。设计思路是Item使用Scroller实现滑动,ListView依据触摸推断是横向滑动还是竖直滑动。关于点击事件处理思路:对于View的onClick事件跟寻常一样,里面针对OnItemClick做了处理,推断触摸距离来推断。假设小于5的话,在Item的onTouchEvent方法中的MotionEvent.ACTION_UP里面返回false,这样ListView里面的dispatchTouchEvent的super.dispatchTouchEvent(event)就会返回false,依据x,y获取当前position以及点击的view。调用super.performItemClick(view, position, view.getId());来告诉ListView出发onItemClick事件。
2、Item的代码
package com.jwzhangjie.scrollview; import android.content.Context; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.widget.LinearLayout; import android.widget.Scroller; public class ListItemDelete extends LinearLayout { private Scroller mScroller;// 滑动控制 private float mLastMotionX;// 记住上次触摸屏的位置 private int deltaX; private int back_width; private float downX; public ListItemDelete(Context context) { this(context, null); } public ListItemDelete(Context context, AttributeSet attrs) { super(context, attrs); init(context); } private void init(Context context) { mScroller = new Scroller(context); } @Override public void computeScroll() { if (mScroller.computeScrollOffset()) {// 会更新Scroller中的当前x,y位置 scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); postInvalidate(); } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int count = getChildCount(); for (int i = 0; i < count; i++) { measureChild(getChildAt(i), widthMeasureSpec, heightMeasureSpec); if (i == 1) { back_width = getChildAt(i).getMeasuredWidth(); } } } @Override public boolean onTouchEvent(MotionEvent event) { int action = event.getAction(); float x = event.getX(); switch (action) { case MotionEvent.ACTION_DOWN: Log.e("test", "item ACTION_DOWN"); mLastMotionX = x; downX = x; break; case MotionEvent.ACTION_MOVE: Log.e("test", back_width + " item ACTION_MOVE " + getScrollX()); deltaX = (int) (mLastMotionX - x); mLastMotionX = x; int scrollx = getScrollX() + deltaX; if (scrollx > 0 && scrollx < back_width) { scrollBy(deltaX, 0); } else if (scrollx > back_width) { scrollTo(back_width, 0); } else if (scrollx < 0) { scrollTo(0, 0); } break; case MotionEvent.ACTION_UP: Log.e("test", "item ACTION_UP"); int scroll = getScrollX(); if (scroll > back_width / 2) { scrollTo(back_width, 0); } else { scrollTo(0, 0); } if (Math.abs(x - downX) < 5) {// 这里依据点击距离来推断是否是itemClick return false; } break; case MotionEvent.ACTION_CANCEL: scrollTo(0, 0); break; } return true; } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { int margeLeft = 0; int size = getChildCount(); for (int i = 0; i < size; i++) { View view = getChildAt(i); if (view.getVisibility() != View.GONE) { int childWidth = view.getMeasuredWidth(); // 将内部子孩子横排排列 view.layout(margeLeft, 0, margeLeft + childWidth, view.getMeasuredHeight()); margeLeft += childWidth; } } } }
3、ListView的代码
package com.jwzhangjie.scrollview; import android.content.Context; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.widget.ListView; public class ScrollListviewDelete extends ListView { private float minDis = 10; private float mLastMotionX;// 记住上次X触摸屏的位置 private float mLastMotionY;// 记住上次Y触摸屏的位置 private boolean isLock = false; public ScrollListviewDelete(Context context, AttributeSet attrs) { super(context, attrs); } /** * 假设一个ViewGroup的onInterceptTouchEvent()方法返回true。说明Touch事件被截获, * 子View不再接收到Touch事件。而是转向本ViewGroup的 * onTouchEvent()方法处理。从Down開始,之后的Move,Up都会直接在onTouchEvent()方法中处理。 * 先前还在处理touch event的child view将会接收到一个 ACTION_CANCEL。
* 假设onInterceptTouchEvent()返回false。则事件会交给child view处理。
*/ @Override public boolean onInterceptTouchEvent(MotionEvent ev) { if (!isIntercept(ev)) { return false; } return super.onInterceptTouchEvent(ev); } @Override public boolean dispatchTouchEvent(MotionEvent event) { boolean dte = super.dispatchTouchEvent(event); if (MotionEvent.ACTION_UP == event.getAction() && !dte) {//onItemClick int position = pointToPosition((int)event.getX(), (int)event.getY()); View view = getChildAt(position); super.performItemClick(view, position, view.getId()); } return dte; } @Override // 处理点击事件,假设是手势的事件则不作点击事件 普通View public boolean performClick() { return super.performClick(); } @Override // 处理点击事件。假设是手势的事件则不作点击事件 ListView public boolean performItemClick(View view, int position, long id) { return super.performItemClick(view, position, id); } /** * 检測是ListView滑动还是item滑动 isLock 一旦判读是item滑动。则在up之前都是返回false */ private boolean isIntercept(MotionEvent ev) { float x = ev.getX(); float y = ev.getY(); int action = ev.getAction(); switch (action) { case MotionEvent.ACTION_DOWN: Log.e("test", "isIntercept ACTION_DOWN "+isLock); mLastMotionX = x; mLastMotionY = y; break; case MotionEvent.ACTION_MOVE: Log.e("test", "isIntercept ACTION_MOVE "+isLock); if (!isLock) { float deltaX = Math.abs(mLastMotionX - x); float deltay = Math.abs(mLastMotionY - y); mLastMotionX = x; mLastMotionY = y; if (deltaX > deltay && deltaX > minDis) { isLock = true; return false; } } else { return false; } break; case MotionEvent.ACTION_UP: Log.e("test", "isIntercept ACTION_UP "+isLock); isLock = false; break; case MotionEvent.ACTION_CANCEL: Log.e("test", "isIntercept ACTION_CANCEL "+isLock); isLock = false; break; } return true; } }
4、Activity代码
package com.jwzhangjie.scrollview; import java.util.ArrayList; import java.util.List; import android.os.Bundle; import android.support.v4.app.FragmentActivity; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.BaseAdapter; import android.widget.Button; import android.widget.TextView; import android.widget.Toast; public class ScrollerDeleteActivity extends FragmentActivity implements OnItemClickListener { private Toast mToast; private ScrollListviewDelete listviewDelete; private DeleteAdapter adapter; private String[] datas = { "第一项", "第二项", "第三项", "第四项", "第五项", "第六项", "第七项", "第八项", "第九项", "第十项" }; private List<String> listDatas = new ArrayList<String>(); @Override protected void onCreate(Bundle bundle) { super.onCreate(bundle); setContentView(R.layout.activity_scroller_delete); int len = datas.length; for (int i = 0; i < len; i++) { listDatas.add(datas[i]); } listviewDelete = (ScrollListviewDelete) findViewById(android.R.id.list); adapter = new DeleteAdapter(); listviewDelete.setAdapter(adapter); listviewDelete.setOnItemClickListener(this); } class DeleteAdapter extends BaseAdapter { @Override public int getCount() { return listDatas.size(); } @Override public Object getItem(int position) { return listDatas.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(final int position, View convertView, ViewGroup parent) { ViewHolder holder; if (convertView == null) { holder = new ViewHolder(); convertView = LayoutInflater.from(getApplicationContext()) .inflate(R.layout.item_delete, null); holder.itemData = (TextView) convertView .findViewById(R.id.itemData); holder.btnDelete = (Button) convertView .findViewById(R.id.btnDelete); holder.btnNao = (Button) convertView.findViewById(R.id.btnNao); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } holder.btnDelete.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { showInfo("点击删除了"); } }); holder.itemData.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { showInfo("点击了数据: " + listDatas.get(position)); } }); holder.btnNao.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { showInfo("点击了闹铃"); } }); holder.itemData.setText(listDatas.get(position)); return convertView; } class ViewHolder { TextView itemData; Button btnDelete; Button btnNao; } } @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { showInfo("onItemClick : " + position); } public void showInfo(String text) { if (mToast == null) { mToast = Toast.makeText(this, text, Toast.LENGTH_SHORT); } else { mToast.setText(text); mToast.setDuration(Toast.LENGTH_SHORT); } mToast.show(); } }
5、XML代码
<?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"
android:orientation="vertical" >
<com.jwzhangjie.scrollview.ScrollListviewDelete
android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<com.jwzhangjie.scrollview.ListItemDelete xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<LinearLayout
android:id="@+id/front"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/bg_item_list_8"
android:gravity="center"
android:orientation="horizontal" >
<TextView
android:id="@+id/itemData"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="測试数据" />
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal" >
<Button
android:id="@+id/btnNao"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:background="@drawable/bg_item_list_4"
android:text="闹铃" />
<Button
android:id="@+id/btnDelete"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:background="@drawable/bg_item_list_5"
android:text="删除" />
</LinearLayout>
</com.jwzhangjie.scrollview.ListItemDelete>
6、界面效果
不保证上面的代码是最新的。更新代码地址:https://github.com/jwzhangjie/-ScrollerDelete
V1.0版本号:解决
地址:https://github.com/jwzhangjie/-ScrollerDelete 里面有编译好的apk