先上效果图
分析下整个效果如何实现
1.每一个item的界面如何去控制,做出类似动画一样的效果
2.对onTouchEvent事件的处理
想下,是不是只要每一个item处理好,做出这种效果之后,那么剩下的问题就相对简单多了
那好首先我们来看下,如何来实现item的侧滑效果
我们的每一个itme的布局都是由两部分组成一个是主view一个是侧滑view,所以这两个view我们需要在构造方法中传入,并且,我们不需要在xml文件中添加,所以只需要实现一参的构造方法就行了
package wkk.demo15;
import android.support.v4.widget.ScrollerCompat;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.LinearLayout;
/**
* Created by wkk on 2016/6/12.
*/
public class SlidingMenuViewGroup extends LinearLayout {
//实现滚动操作
private ScrollerCompat scrollerCompat;
//按下的x点坐标
private int xDown;
//主布局以及菜单布局
private View contentView;
private View menuView;
//菜单是否打开
private boolean isOpen;
//1构造方法,这里传入两个view,一个是主布局,一个是侧面的布局
public SlidingMenuViewGroup(View contentView, View menuView) {
super(contentView.getContext());
this.contentView = contentView;
this.menuView = menuView;
init();
}
private void init() {
setLayoutParams(new AbsListView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
scrollerCompat = ScrollerCompat.create(getContext());
contentView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
menuView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
addView(contentView);
addView(menuView);
}
}
以上代码并不难理解,不过是定义一些所必要的参数,并且初始化一些数据,这里就不多做赘述
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//设置menuView的测量模式
//此处宽必须设置为UNSPECIFIED 否则将不会绘制
menuView.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)
, MeasureSpec.makeMeasureSpec(menuView.getMeasuredHeight(), MeasureSpec.EXACTLY));
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
//指定contentview和menuView的位置
contentView.layout(0, 0, getMeasuredWidth(), getMeasuredHeight());
menuView.layout(getMeasuredWidth(), 0, getMeasuredWidth() + menuView.getMeasuredWidth(), getMeasuredHeight());
}
MainActivity代码:
package wkk.demo15;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.ViewGroup;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setView();
}
private void setView() {
TextView textView1 = new TextView(this);
textView1.setText("content");
textView1.setGravity(Gravity.CENTER);
textView1.setBackgroundColor(0xffff123f);
textView1.setHeight(dp2px(70));
TextView textView2 = new TextView(this);
textView2.setGravity(Gravity.CENTER);
textView2.setWidth(dp2px(70));
textView2.setHeight(dp2px(70));
textView2.setText("menu");
textView2.setBackgroundColor(0xbbbbbbbb);
SlidingMenuViewGroup sildingMenuViewGroup = new SlidingMenuViewGroup(textView1, textView2);
addContentView(sildingMenuViewGroup, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, dp2px(70)));
}
//尺寸转化
private int dp2px(int dp) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp,
getResources().getDisplayMetrics());
}
}
此时效果如上图,如果用鼠标点击,并没有任何作用,当然,这些都是准备工作,接下来进行关键的onTouchEvent的事件处理
@Override
public boolean onTouchEvent(MotionEvent event) {
onEvent(event);
return true;
}
public void onEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
//记录坐标
xDown = (int) event.getX();
break;
case MotionEvent.ACTION_MOVE:
//滑动的距离
int distance = (int) (xDown - event.getX());
if (isOpen) {
//此处需要弄清楚getWidth和getMeasuredWidth的区别
//getWidth的到的宽度是显示在屏幕中的部分
//getMeasuredWidth,如果有屏幕之外的部分即,未显示的部分,那么getMeasuredWidth的到宽度是
//实际宽度即显示在屏幕中的宽度和屏幕之外的宽度,如果在屏幕中已近完全显示,那么得到的部分即和getWidth相同
distance += menuView.getWidth();
}
sliding(distance);
break;
case MotionEvent.ACTION_UP:
break;
}
}
//滑动
private void sliding(int distance) {
//控制最大最小距离
if (distance < 0) {
distance = 0;
}
if (distance > menuView.getMeasuredWidth()) {
distance = menuView.getMeasuredWidth();
}
//重新设置位置
contentView.layout(-distance, 0, getWidth() - distance, getHeight());
menuView.layout(getWidth() - distance, 0, getWidth() + menuView.getMeasuredWidth() - distance, getHeight());
}
效果如图:
可以看到,我们所要的效果已经有了雏形,但是,滑动并不流畅,当然,或许你已经注意到我们在上边定义的ScrollerCompat,并没有用到,那么接下来,对up事件进行处理的时候就会用到ScrollerCompat
case MotionEvent.ACTION_UP:
//判断滑动距离是否大于menuview的一半
// 如果大于就打开,否则就关闭菜单
if ((xDown - event.getX()) > (menuView.getMeasuredWidth() / 2)) {
openMenu();
} else {
closeMenu();
}
break;
public void closeMenu() {
isOpen = false;
//参数:x轴起始位置,y轴起始位置,x轴偏移量,y轴偏移量,时间
//偏移量为正时,向右移动,偏移量为负时,向右移动
scrollerCompat.startScroll(contentView.getLeft(), 0, -contentView.getLeft(), 0, 300);
//通知重绘
postInvalidate();
}
public void openMenu() {
isOpen = true;
//再调用这个方法后view就会调用computeScroll方法,所以要对其进行重写
scrollerCompat.startScroll(contentView.getLeft(), 0, -(menuView.getWidth() + contentView.getLeft()), 0, 300);
postInvalidate();
}
@Override
public void computeScroll() {
super.computeScroll();
//判断是否完成滑动
if (scrollerCompat.computeScrollOffset()) {
//scrollerCompat.getCurrX() 获取当前x轴的滑动位置
sliding(Math.abs(scrollerCompat.getCurrX()));
postInvalidate();
}
}
效果如下图
可以看到效果已经是我们想要的效果了,而且滑动也很流畅了,这里在添加下对外部提供的方法:
//对外部提供方法
public boolean isOpen() {
return isOpen;
}
public View getMenuView(){
return menuView;
}
好,至此,item已经基本完成了,可是,这当然不是最终效果,对onTouchEvent事件,我们当然不可以全部拦截,还有如果你设置了contentview的点击事件,你会发现,滑动就失效了,所以,我门还要自定义listview,如果你对listview的onTouchEvent事件不去处理的话,直接使用原生,就会发现很多奇怪的现象,并不是我们想要的效果,如果有兴趣,可以去试试看呐
首先将SlidingMenuViewGroup的onTouchEvent事件还原
@Override
public boolean onTouchEvent(MotionEvent event) {
return super.onTouchEvent(event);
}
重写ListView,定义相关变量
package wkk.demo15;
import android.content.Context;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.widget.ListView;
/**
* Created by wkk on 2016/6/12.
*/
public class SlidingMenuListView extends ListView {
//滑动最小值
private int MIN_X;
private int MIN_Y;
//按下点坐标
private int xDown;
private int yDown;
//当前的子view
private SlidingMenuViewGroup sildingMenu;
//手指所在item位置
private int position;
private int oldposition;
//移动状态
private int touchState;
private int TOUCK_STATE_NULL = 0;
private int TOUCH_STATE_X = 1;
private int TOUCH_STATE_Y = 2;
//移动距离
private int dx;
private int dy;
private boolean is;
public SlidingMenuListView(Context context, AttributeSet attrs) {
super(context, attrs);
MIN_X = dp2px(3);
MIN_Y = dp2px(5);
}
//尺寸转化
private int dp2px(int dp) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp,
getResources().getDisplayMetrics());
}
}
最关键的onTouchEvent事件处理:
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
//首先记录坐标
xDown = (int) ev.getX();
yDown = (int) ev.getY();
oldposition = position;
//状态设置为null
touchState = TOUCK_STATE_NULL;
is = false;
position = pointToPosition(xDown, yDown);
//如果两次按下时的位置相同,并且菜单是打开的
if (oldposition == position && sildingMenu != null && sildingMenu.isOpen()) {
touchState = TOUCH_STATE_X;
sildingMenu.onEvent(ev);
return true;
}
//如果两次按下的位置不同,并且菜单是打开的
if (sildingMenu != null && sildingMenu.isOpen()) {
//直接关闭菜单
sildingMenu.closeMenu();
sildingMenu = null;
//这种情况下,不去响应up事件
is = true;
return true;
}
//得到view
View view = getChildAt(position - getFirstVisiblePosition());
//判断view是否是SildingMenuViewGroup的对象
if (view instanceof SlidingMenuViewGroup) {
sildingMenu = (SlidingMenuViewGroup) view;
}
if (sildingMenu != null) {
sildingMenu.onEvent(ev);
}
break;
case MotionEvent.ACTION_MOVE:
//取得xy轴的偏移量
dx = (int) (xDown - ev.getX());
dy = (int) Math.abs(ev.getY() - yDown);
if (touchState == TOUCK_STATE_NULL) {
//判断 设置状态 优先判断Y
if (dy > MIN_Y) {
touchState = TOUCH_STATE_Y;
} else if (dx > MIN_X) {
touchState = TOUCH_STATE_X;
}
} else if (touchState == TOUCH_STATE_X) {
//将事件传递给sildingMenu
if (sildingMenu != null) {
sildingMenu.onEvent(ev);
}
//取消,不处理
ev.setAction(MotionEvent.ACTION_CANCEL);
super.onTouchEvent(ev);
return true;
}
break;
case MotionEvent.ACTION_UP:
if (touchState == TOUCH_STATE_X) {
if (sildingMenu != null) {
sildingMenu.onEvent(ev);
//如果菜单是关的 重置
if (!sildingMenu.isOpen()) {
position = -1;
sildingMenu = null;
}
}
ev.setAction(MotionEvent.ACTION_CANCEL);
super.onTouchEvent(ev);
return true;
}
if (touchState == TOUCK_STATE_NULL) {
if (is) {
ev.setAction(MotionEvent.ACTION_CANCEL);
super.onTouchEvent(ev);
return true;
}
}
break;
}
return super.onTouchEvent(ev);
}
至此,我们的ListView已经编写完成了,但是这样就行了吗?No,我们还有去对适配器进行修改,并且响应删除菜单的点击事件
MyBaseAdapter:
http://blog.csdn.net/w18756901575/article/details/51028177
Adapter:
package wkk.demo15;
import android.content.Context;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
/**
* Created by wkk on 2016/6/12.
*/
public class DemoAdapter extends MyBaseAdapter<String> {
private MenuClickListener listener;
public DemoAdapter(Context context) {
super(context);
}
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
SlidingMenuViewGroup viewGroup = null;
ViewHold viewHold = null;
if (convertView == null) {
viewHold = new ViewHold();
View contentview = inflater.inflate(R.layout.item, null);
TextView textView = new TextView(context);
textView.setWidth(getDp(80));
textView.setHeight(getDp(80));
textView.setBackgroundColor(0xbbbbbbbb);
textView.setGravity(Gravity.CENTER);
textView.setText("删除");
textView.setTag(0);
TextView textView1 = new TextView(context);
textView1.setWidth(getDp(80));
textView1.setHeight(getDp(80));
textView1.setBackgroundColor(0xbbff123f);
textView1.setGravity(Gravity.CENTER);
textView1.setText("测试");
textView1.setTag(1);
LinearLayout menu = new LinearLayout(context);
menu.setLayoutParams(new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT
, ViewGroup.LayoutParams.MATCH_PARENT));
menu.addView(textView);
menu.addView(textView1);
viewHold.text = (TextView) contentview.findViewById(R.id.text);
viewGroup = new SlidingMenuViewGroup(contentview, menu);
viewGroup.setTag(viewHold);
} else {
viewGroup = (SlidingMenuViewGroup) convertView;
viewHold = (ViewHold) viewGroup.getTag();
}
viewHold.text.setText(getItem(position));
final ViewGroup menu = (ViewGroup) viewGroup.getMenuView();
final SlidingMenuViewGroup finalViewGroup = viewGroup;
int count = menu.getChildCount();
for (int i = 0; i < count; i++) {
View v = menu.getChildAt(i);
v.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//将点击事件传递出去
if (listener != null) {
int tag = (int) v.getTag();
listener.onMenuClickListener(v, position, finalViewGroup, tag);
finalViewGroup.closeMenu();
}
}
});
}
return viewGroup;
}
private class ViewHold {
TextView text;
}
//提供外部监听
public void setListener(MenuClickListener listener) {
this.listener = listener;
}
public interface MenuClickListener {
void onMenuClickListener(View menu, int position, SlidingMenuViewGroup viewGroup, int tag);
}
private int getDp(int dp) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp,
context.getResources().getDisplayMetrics());
}
}
MainActivity:
package wkk.demo15;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.TextView;
import android.widget.Toast;
import java.util.List;
public class MainActivity extends AppCompatActivity implements AdapterView.OnItemClickListener {
private SlidingMenuListView listView;
private DemoAdapter adapter;
private List<String> list;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listView = (SlidingMenuListView) findViewById(R.id.listview);
adapter = new DemoAdapter(this);
list = adapter.getList();
for (int i = 0; i < 20; i++) {
list.add("这是一条测试数据: " + String.valueOf(i));
}
listView.setAdapter(adapter);
listView.setOnItemClickListener(this);
adapter.setListener(new DemoAdapter.MenuClickListener() {
@Override
public void onMenuClickListener(View menu, int position, SlidingMenuViewGroup viewGroup, int tag) {
TextView textView = (TextView) menu;
Toast.makeText(MainActivity.this, "点击了" + textView.getText(), Toast.LENGTH_SHORT).show();
if (tag == 0) {
list.remove(position);
adapter.notifyDataSetChanged();
}
}
});
}
private int dp2px(int dp) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp,
getResources().getDisplayMetrics());
}
private void setView() {
TextView textView1 = new TextView(this);
textView1.setText("content");
textView1.setGravity(Gravity.CENTER);
textView1.setBackgroundColor(0xffff123f);
textView1.setHeight(dp2px(70));
TextView textView2 = new TextView(this);
textView2.setGravity(Gravity.CENTER);
textView2.setWidth(dp2px(70));
textView2.setHeight(dp2px(70));
textView2.setText("menu");
textView2.setBackgroundColor(0xbbbbbbbb);
SlidingMenuViewGroup sildingMenuViewGroup = new SlidingMenuViewGroup(textView1, textView2);
addContentView(sildingMenuViewGroup, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, dp2px(70)));
}
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Toast.makeText(this, "点击了" + String.valueOf(position), Toast.LENGTH_SHORT).show();
}
}
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="wkk.demo14.MainActivity">
<wkk.demo15.SlidingMenuListView
android:id="@+id/listview"
android:layout_width="match_parent"
android:layout_height="match_parent">
</wkk.demo15.SlidingMenuListView>
</LinearLayout>
item.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="80dp"
android:gravity="center">
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="这是一条测试数据" />
</LinearLayout>
至此,整个代码就编写完成了,效果图已经在一开始就贴了出来,这里就不贴了,当然,有兴趣的可以对整个Demo进行进一步的封装
Demo下载地址:
http://download.csdn.net/detail/w18756901575/9546912