嗨 大家好,我是不服不行 。
今天为大家带来一个手机界面中的这个,这个一个常客。经常被使用在列表管理之中,让我们先看看效果。
实现方式:
1首先把listview中的每项内容都看做2层,展现的内容[content]在上面而菜单[menu]在下面,所以使用到的布局便是FrameLayout。
public class ItemManageLayout extends FrameLayout { private FrameLayout layMenu; private SlidingFrameLayout layContent; public ItemManageLayout(Context context) { this(context, null); } public ItemManageLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public ItemManageLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); layMenu = new FrameLayout(context); LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT); lp.gravity = Gravity.RIGHT; layMenu.setLayoutParams(lp); layContent = new SlidingFrameLayout(context); addView(layMenu); addView(layContent); } ...... public static class SlidingFrameLayout extends FrameLayout { ...... } }
细心的朋友注意到了。content并不简单是一个FrameLayout,而是SlidingFrameLayout 看名字便知道,我们的content是具有可滑动的样子,是不是叼叼哒。
那SlidingFrameLayout是什么呢?来看看吧:
public static class SlidingFrameLayout extends FrameLayout { private Scroller mScroller; private boolean isAnim = false; private boolean isIntercepter = false; private boolean is2OpenMenu = false; private boolean isMenuOpened = false; private int mDuration = 400; private int menusWidth; private int touchSlop; private float downX; public SlidingFrameLayout(Context context) { this(context, null); } public SlidingFrameLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public SlidingFrameLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); mScroller = new Scroller(context, new DecelerateInterpolator()); touchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); } public void setMenusWidth(int menusWidth) { this.menusWidth = menusWidth; } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_MOVE: if (downX - event.getRawX() < 0 && getScrollX() <= 0) { return true; } scrollTo((int) (downX - event.getRawX() + (isMenuOpened ? menusWidth : 0) / 2), 0); break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: is2OpenMenu = downX - event.getRawX() > menusWidth / 2; dealTouchMoveResult(); break; } return super.onTouchEvent(event); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { if (isAnim) { return true; } switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: downX = ev.getRawX(); break; case MotionEvent.ACTION_MOVE: if (downX - ev.getRawX() > touchSlop) { isIntercepter = true; } if (isIntercepter) { return true; } break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: isIntercepter = false; break; } return super.onInterceptTouchEvent(ev); } private void dealTouchMoveResult() { if (is2OpenMenu) { startMoveAnim(getScrollX(), -getScrollX() + menusWidth, mDuration); isMenuOpened = true; } else { startMoveAnim(getScrollX(), -getScrollX(), mDuration); isMenuOpened = false; } } private void startMoveAnim(int startX, int dx, int duration) { isAnim = true; mScroller.startScroll(startX, 0, dx, 0, duration); invalidate(); } @Override public void computeScroll() { if (mScroller.computeScrollOffset()) { scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); postInvalidate(); isAnim = true; } else { isAnim = false; } super.computeScroll(); } }
之后需要做得便是监听content的触摸事件。于是一个头疼的问题产生了,触摸content的结果是点击还是滑动?那么为了解决这个问题,我们就需要重写onInterceptTouchEvent(),其中逻辑也很简单,在MOVE的时候如果手指是向左移动并且超过了一定距离那么我们就拦截掉处理自己的滑动事件,不然交给child处理(比如点击)。
那么在onTouchEvent中。MOVE 使用scrollTo()产生随手指的移动,最后UP时候调用dealTouchMoveResult() 来控制手指抬起后的移动,menu是开是关全在这里。
好了。现在附上整体代码:
public class ItemManageLayout extends FrameLayout { private FrameLayout layMenu; private SlidingFrameLayout layContent; public ItemManageLayout(Context context) { this(context, null); } public ItemManageLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public ItemManageLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); layMenu = new FrameLayout(context); LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT); lp.gravity = Gravity.RIGHT; layMenu.setLayoutParams(lp); layContent = new SlidingFrameLayout(context); addView(layMenu); addView(layContent); post(new Runnable() { @Override public void run() { layContent.setMenusWidth(layMenu.getChildAt(0).getWidth()); } }); } public void setMenu(View menu) { layMenu.removeAllViews(); layMenu.addView(menu); } public void setContent(View content) { layContent.removeAllViews(); layContent.addView(content); content.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { if (layContent.isMenuOpened) { layContent.is2OpenMenu = false; layContent.dealTouchMoveResult(); return true; } return false; } }); } public static class SlidingFrameLayout extends FrameLayout { private Scroller mScroller; private boolean isAnim = false; private boolean isIntercepter = false; private boolean is2OpenMenu = false; private boolean isMenuOpened = false; private int mDuration = 400; private int menusWidth; private int touchSlop; private float downX; public SlidingFrameLayout(Context context) { this(context, null); } public SlidingFrameLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public SlidingFrameLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); mScroller = new Scroller(context, new DecelerateInterpolator()); touchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); } public void setMenusWidth(int menusWidth) { this.menusWidth = menusWidth; } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_MOVE: if (downX - event.getRawX() < 0 && getScrollX() <= 0) { return true; } scrollTo((int) (downX - event.getRawX() + (isMenuOpened ? menusWidth : 0) / 2), 0); break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: is2OpenMenu = downX - event.getRawX() > menusWidth / 2; dealTouchMoveResult(); break; } return super.onTouchEvent(event); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { if (isAnim) { return true; } switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: downX = ev.getRawX(); break; case MotionEvent.ACTION_MOVE: if (downX - ev.getRawX() > touchSlop) { isIntercepter = true; } if (isIntercepter) { return true; } break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: isIntercepter = false; break; } return super.onInterceptTouchEvent(ev); } private void dealTouchMoveResult() { if (is2OpenMenu) { startMoveAnim(getScrollX(), -getScrollX() + menusWidth, mDuration); isMenuOpened = true; } else { startMoveAnim(getScrollX(), -getScrollX(), mDuration); isMenuOpened = false; } } private void startMoveAnim(int startX, int dx, int duration) { isAnim = true; mScroller.startScroll(startX, 0, dx, 0, duration); invalidate(); } @Override public void computeScroll() { if (mScroller.computeScrollOffset()) { scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); postInvalidate(); isAnim = true; } else { isAnim = false; } super.computeScroll(); } } }
...... final List<String> list = new ArrayList<String>(); list.add(""); list.add(""); listView.setAdapter(new BaseAdapter() { class ViewHolder { View menu; View content; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder viewHolder; if (convertView != null) { viewHolder = (ViewHolder) convertView.getTag(); } else { convertView = new ItemManageLayout(MainActivity.this); viewHolder = new ViewHolder(); viewHolder.menu = LayoutInflater.from(MainActivity.this).inflate(R.layout.test_manage_menu, null); viewHolder.content = LayoutInflater.from(MainActivity.this).inflate(R.layout.test_manage_content, null); convertView.setTag(viewHolder); } viewHolder.menu.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Toast.makeText(getApplicationContext(), "menu click", Toast.LENGTH_SHORT).show(); } }); viewHolder.content.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Toast.makeText(getApplicationContext(), "content click", Toast.LENGTH_SHORT).show(); } }); ((ItemManageLayout) convertView).setMenu(viewHolder.menu); ((ItemManageLayout) convertView).setContent(viewHolder.content); return convertView; } @Override public long getItemId(int position) { return position; } @Override public Object getItem(int position) { return list.get(position); } @Override public int getCount() { return list.size(); } }); ......
test_manage_content.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@drawable/selector_item_manage" android:gravity="center_vertical" android:orientation="vertical" android:padding="5dip" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="驸马水立方湖南师大看那个V刊时代" android:textColor="#515151" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="dajldas打算" android:textColor="#9e9e9e" /> </LinearLayout>
test_manage_menu:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="#00000000" > <FrameLayout android:layout_width="wrap_content" android:layout_height="match_parent" android:background="#cf640b" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:padding="10dip" android:text="删除" android:textColor="#FFF" /> </FrameLayout> </LinearLayout>