今天想试试android View的事件分发机制,于是自制了一个仿QQ的侧滑删除当小白鼠,利用这个来验证View面对不同的情况下,的事件分发情况。
由于是当给自己当试验品用,做的有点简陋,不对那可是相当简陋。
原理:首先一个全屏的item加上一个button拼接成一个HorizontalScrollView。让button显示在屏幕的外面,当侧滑的时候显示出button。之后放入listview中
效果图:![仿QQ侧滑删除](https://img-blog.csdn.net/20151209164737710)
首先还是自定义View第一步:values 下的attr
<?xml version="1.0" encoding="utf-8"?>
<resources>
<attr name="width_ethan" format="dimension"/>
<declare-styleable name="Ethan">
<attr name="width_ethan"/>
</declare-styleable>
</resources>
attr 定义了一个宽度,这个宽度也就是后来的删除button的宽度,也是侧滑的最大距离。
接下来就是自定义MyView了
package com.example.tencent;
import com.example.day.activity.utils.ScreenUtils;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.View;
import android.widget.HorizontalScrollView;
import android.widget.LinearLayout;
public class MyView extends HorizontalScrollView{
private int width;
private int mScreenWidth;//屏幕宽度
private float halfwidth;
public MyView(Context context, AttributeSet attrs) {
this(context, attrs,0);
mScreenWidth=ScreenUtils.getScreenWidth(context);
}
public MyView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
TypedArray a=getContext().getTheme().obtainStyledAttributes(attrs,R.styleable.Ethan,defStyle,0);
int count=a.getIndexCount();
for(int i=0;i<count;i++)
{
int attr=a.getIndex(i);
switch (attr) {
case R.styleable.Ethan_width_ethan:
width=a.getDimensionPixelSize(attr,(int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 20, getResources().getDisplayMetrics()));
halfwidth=width/2;
break;
}
a.recycle();
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
LinearLayout ly = (LinearLayout) getChildAt(0);
View list=ly.getChildAt(0);
View button=ly.getChildAt(1);
button.getLayoutParams().width=width;
list.getLayoutParams().width=mScreenWidth;
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
// TODO 自动生成的方法存根
return false;
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
super.onTouchEvent(ev);
int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_UP:
int scrollX = getScrollX();
if(Math.abs(scrollX)>halfwidth)
{
this. smoothScrollTo(width,0);
}
else
{
this. smoothScrollTo(0,0);
}
break;
}
//return false;
return true;
//return super.onTouchEvent(ev);
}
}
MyView里面首先是二参数构造方法来调用三参数的构造方法,并且获取了屏幕的宽度大小。接着三个参数构造方法拿到typedArray 对象a。a也就对应着attr里面对应的自定义列表项。通过a可以拿到定义的width_ethan的值,也就是自定义删除按钮的宽度。
紧接着在onMeasure()方法中拿到该布局下的两个子View(item和button)的对象,
然后设置两个对象的LayoutParams,之后进行测量。测量调用的view中的LayoutParams就是设置的。
测量完成后重写MyView的onTouchEvent()方法,首先拿到当前的点击事件,当
ACTION_UP执行时,也就是一次用户手操作完毕时。判断滑动的距离,如果距离大于删除button的一般就采用smoothScrollTo平滑过去,反之回到原状态。
注意:onTouchEvent()返回值一定要为true,否则该方法执行不了,返回到上一层次的onTouchEvent()方法(View的事件分发机制);
之后主布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="450dp"
android:layout_height="50dp"
android:id="@+id/list"
android:background="@android:color/holo_purple"
android:orientation="vertical" >
<TextView
android:id="@+id/title_text"
android:text="标题"
android:textSize="20sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/context_text"
android:text="内容"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
<RelativeLayout 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:id="@+id/layout"
android:focusable="true"
android:focusableInTouchMode="true"
tools:context=".MainActivity" >
<ListView
android:focusable="true"
android:focusableInTouchMode="true"
android:id="@+id/listview"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
</ListView>
</RelativeLayout>
接下来就是MyAdapter了
package com.example.tencent;
import java.util.List;
import android.content.Context;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.LinearLayout;
import android.widget.TextView;
public class MyAdapter extends BaseAdapter {
private List<Item> list;
private LayoutInflater layoutInflater;
public MyAdapter(Context context, List<Item> list) {
this.list = list;
layoutInflater = LayoutInflater.from(context);
}
@Override
public int getCount() {
return list.size();
}
@Override
public Object getItem(int position) {
return list.get(position);
}
@Override
public long getItemId(int position) {
// TODO 自动生成的方法存根
return position;
}
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder;
if (convertView == null) {
viewHolder = new ViewHolder();
convertView = layoutInflater.inflate(R.layout.my_view, null);
viewHolder.title = (TextView) convertView
.findViewById(R.id.title_text);
viewHolder.context = (TextView) convertView
.findViewById(R.id.context_text);
viewHolder.button = (TextView) convertView
.findViewById(R.id.del_button);
viewHolder.ly=(LinearLayout) convertView.findViewById(R.id.list);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
viewHolder.title.setText(list.get(position).getName());
viewHolder.context.setText(list.get(position).getAge());
viewHolder.ly.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
Log.i("ethan", "ly ontouch");
return false;
}
});
viewHolder.button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
list.remove(position);
notifyDataSetChanged();
}
});
return convertView;
}
class ViewHolder {
TextView title, context;
TextView button;
LinearLayout ly;
}
}
MyAdapter里面就是一般的实现BaseAdapter的步骤。这里对button设置了一个点击事件来移除list,同时对布局设置了一个ontouch(该方法纯属测试View的事件分发用);
接下来就是MainActivity了
package com.example.tencent;
import java.util.ArrayList;
import java.util.List;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;
import android.view.Window;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.Toast;
public class MainActivity extends Activity
{
private List<Item>list;
private ListView listview;
private RelativeLayout rl;
private View myView;
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO 自动生成的方法存根
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main);
listview=(ListView) findViewById(R.id.listview);
rl=(RelativeLayout) findViewById(R.id.layout);
list=new ArrayList<Item>();
MyAdapter my=new MyAdapter(this, list);
for(int i=0;i<11;i++)
{
Item item=new Item();
item.setName("Ethan");
item.setAge(i+"");
list.add(item);
}
listview.setAdapter(my);
listview.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
Log.e("ethan", "listview on touch run");
return false;
}
});
listview.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
// TODO 自动生成的方法存根
Log.e("ethan", "listview on click run");
Toast.makeText(MainActivity.this, "success", Toast.LENGTH_SHORT).show();
}
});
}
}
MainActivity里面就是一般的listview的setAdapter方法。中间的两个setOnTouchListener和setOnItemClickListener是作为测试View的事件分发用的。
OK 大功告成,简陋版仿QQ侧滑删除。