效果图:
自定义View的功能到此就完成了,接下来我们需要看一下如何才能使用这个自定义View。首先需要创建一个ListView子项的布局文件,新建listview_item.xml,代码如下所示:
然后创建一个适配器ListItemAdapter,在这个适配器中去加载listview_item.xml布局,代码如下所示:
最后在Activity中初始化ListViewDel中的数据,代码如下所示:
(本篇完)
实现:
1.新建ListViewDel 继承自ListView,即自定义的View了,代码如下所示:
package com.util;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.animation.Animation;
import android.view.animation.Animation.AnimationListener;
import android.view.animation.ScaleAnimation;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.Scroller;
import com.blog.R;
public class ListViewDel extends ListView {
private int slidePosition, preSlidePosition;
private int downY;
private int downX;
public static View itemView, preItemView;
private Scroller scroller;
private static final int SNAP_VELOCITY = 600;
private VelocityTracker velocityTracker;
public static boolean isSlide = false;
private boolean isResponse = true;
public static boolean isObstruct = true;
private int mTouchSlop;
private RemoveListener mRemoveListener;
private static Animation scaleHideAnimation;
private static Animation scaleShowAnimation;
public ListViewDel(Context context) {
this(context, null);
}
public ListViewDel(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ListViewDel(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
scroller = new Scroller(context);
mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
}
public void setRemoveListener(RemoveListener removeListener) {
this.mRemoveListener = removeListener;
}
public boolean dispatchTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
addVelocityTracker(event);
downX = (int) event.getX();
downY = (int) event.getY();
slidePosition = pointToPosition(downX, downY);
if (slidePosition == AdapterView.INVALID_POSITION) {
return super.dispatchTouchEvent(event);
}
if (preItemView != null
&& preItemView.findViewById(R.id.tv_coating)
.getVisibility() == View.GONE) {
itemView = preItemView;
slidePosition = preSlidePosition;
} else {
itemView = getChildAt(slidePosition - getFirstVisiblePosition());
preItemView = itemView;
preSlidePosition = slidePosition;
}
break;
}
case MotionEvent.ACTION_MOVE: {
if (Math.abs(getScrollVelocity()) > SNAP_VELOCITY
|| (Math.abs(event.getX() - downX) > mTouchSlop && Math
.abs(event.getY() - downY) < mTouchSlop)) {
isSlide = true;
}
break;
}
case MotionEvent.ACTION_UP:
recycleVelocityTracker();
isObstruct = true;
break;
}
return super.dispatchTouchEvent(event);
}
public boolean onInterceptTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_MOVE:
if (itemView.findViewById(R.id.tv_coating).getVisibility() == View.VISIBLE) {
isSlide = false;
} else {
isSlide = true;
}
break;
default:
break;
}
return super.onInterceptTouchEvent(ev);
}
public boolean onTouchEvent(MotionEvent ev) {
if (isSlide && slidePosition != AdapterView.INVALID_POSITION) {
addVelocityTracker(ev);
final int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_MOVE:
if (isObstruct) {
if (itemView.findViewById(R.id.tv_coating).getVisibility() == View.VISIBLE
&& isResponse == true) {
scaleHideAnimation = new ScaleAnimation(1.0f, 0.0f,
1.0f, 1.0f);
scaleHideAnimation.setDuration(250);
scaleHideAnimation
.setAnimationListener(new AnimationListener() {
public void onAnimationStart(
Animation animation) {
isResponse = false;
isObstruct = false;
}
public void onAnimationRepeat(
Animation animation) {
}
public void onAnimationEnd(
Animation animation) {
isResponse = true;
itemView.findViewById(R.id.tv_coating)
.setVisibility(View.GONE);
itemView.findViewById(R.id.tv_functions)
.setClickable(true);
itemView.findViewById(R.id.tv_functions)
.setOnClickListener(
new OnClickListener() {
public void onClick(
View v) {
mRemoveListener
.removeItem(slidePosition);
}
});
}
});
itemView.findViewById(R.id.tv_coating).startAnimation(
scaleHideAnimation);
} else if (itemView.findViewById(R.id.tv_coating)
.getVisibility() == View.GONE && isResponse == true) {
scaleShowAnimation = new ScaleAnimation(0.0f, 1.0f,
1.0f, 1.0f);
scaleShowAnimation.setDuration(250);
scaleShowAnimation
.setAnimationListener(new AnimationListener() {
public void onAnimationStart(
Animation animation) {
isResponse = false;
isObstruct = false;
}
public void onAnimationRepeat(
Animation animation) {
}
public void onAnimationEnd(
Animation animation) {
isSlide = false;
isResponse = true;
itemView.findViewById(R.id.tv_coating)
.setVisibility(View.VISIBLE);
}
});
itemView.findViewById(R.id.tv_coating).startAnimation(
scaleShowAnimation);
}
}
break;
case MotionEvent.ACTION_UP:
isObstruct = true;
recycleVelocityTracker();
isSlide = true;
break;
}
return true;
}
return super.onTouchEvent(ev);
}
public void computeScroll() {
if (scroller.computeScrollOffset()) {
postInvalidate();
if (scroller.isFinished()) {
if (mRemoveListener == null) {
throw new NullPointerException(
"RemoveListener is null, we should called setRemoveListener()");
}
itemView.scrollTo(0, 0);
}
}
}
private void addVelocityTracker(MotionEvent event) {
if (velocityTracker == null) {
velocityTracker = VelocityTracker.obtain();
}
velocityTracker.addMovement(event);
}
private void recycleVelocityTracker() {
if (velocityTracker != null) {
velocityTracker.recycle();
velocityTracker = null;
}
}
private int getScrollVelocity() {
velocityTracker.computeCurrentVelocity(1000);
int velocity = (int) velocityTracker.getXVelocity();
return velocity;
}
public interface RemoveListener {
public void removeItem(int position);
}
}
自定义View的功能到此就完成了,接下来我们需要看一下如何才能使用这个自定义View。首先需要创建一个ListView子项的布局文件,新建listview_item.xml,代码如下所示:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/ll_parent"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#eee"
android:gravity="center"
android:orientation="horizontal"
tools:context=".MainActivity" >
<TextView
android:id="@+id/tv_item"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="left"
android:padding="8dp"
android:text="狗狗"
android:textColor="#000"
android:textSize="16sp" />
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right|center_vertical"
android:padding="15dp" >
<TextView
android:id="@+id/tv_functions"
android:layout_width="100dp"
android:layout_height="30dp"
android:layout_gravity="center"
android:background="@drawable/btn_del_bg"
android:gravity="center"
android:text="删除"
android:textColor="@android:color/white" />
<TextView
android:id="@+id/tv_coating"
android:layout_width="100dp"
android:layout_height="30dp"
android:layout_gravity="center"
android:background="#eee"
android:visibility="gone" />
</FrameLayout>
</LinearLayout>
然后创建一个适配器ListItemAdapter,在这个适配器中去加载listview_item.xml布局,代码如下所示:
package com.util;
import java.util.ArrayList;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
import com.blog.R;
public class ListItemAdapter extends BaseAdapter {
private LayoutInflater inflater;
private ArrayList<String> datas;
public ListItemAdapter(Context context) {
inflater = LayoutInflater.from(context);
}
public void setData(ArrayList<String> datas) {
this.datas = datas;
}
public int getCount() {
return datas.size();
}
public Object getItem(int position) {
return datas.get(position);
}
public long getItemId(int position) {
return position;
}
public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
if (convertView == null) {
holder = new ViewHolder();
convertView = inflater.inflate(R.layout.listview_item, null);
holder.textView = (TextView) convertView.findViewById(R.id.tv_item);
holder.coating = (TextView) convertView
.findViewById(R.id.tv_coating);
holder.functions = (TextView) convertView
.findViewById(R.id.tv_functions);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.textView.setText(datas.get(position));
holder.coating.setVisibility(View.VISIBLE);
holder.functions.setClickable(false);
return convertView;
}
public final class ViewHolder {
public TextView textView;
public TextView coating;
public TextView functions;
}
}
下面在程序的主布局文件里面引入MyListView这个控件,如下所示:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/linearLayout1"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#eee"
android:orientation="vertical" >
<RelativeLayout
android:id="@+id/top_relative"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingBottom="10.0dp" >
<LinearLayout
android:id="@+id/title"
android:layout_width="fill_parent"
android:layout_height="45dp"
android:background="@drawable/android_title_bg"
android:gravity="center_horizontal|center_vertical" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/app_name"
android:textColor="#ffffff"
android:textSize="20sp" />
</LinearLayout>
<TextView
android:id="@+id/typetitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_marginTop="8.0dip"
android:gravity="center"
android:lineSpacingExtra="6.0dp"
android:paddingLeft="20.0dp"
android:paddingTop="10.0dp"
android:singleLine="true"
android:text=""
android:textColor="#fff"
android:textSize="16.0sp" />
</RelativeLayout>
<TextView
android:id="@+id/tv_text"
android:layout_width="match_parent"
android:layout_height="30dp"
android:gravity="center"
android:text="浏览历史记录"
android:textSize="18sp" />
<com.util.ListViewDel
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/tv_text"
android:cacheColorHint="#00000000"
android:divider="@drawable/reader_item_divider"
android:fadingEdge="none"
android:listSelector="#00000000" >
</com.util.ListViewDel>
</LinearLayout>
最后在Activity中初始化ListViewDel中的数据,代码如下所示:
package com.blog;
import java.util.ArrayList;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import com.util.ListItemAdapter;
import com.util.ListViewDel;
import com.util.ListViewDel.RemoveListener;
public class MainActivity extends Activity implements RemoveListener {
private ListViewDel list;
private ListItemAdapter adapter;
private ArrayList<String> dataSourceList = new ArrayList<String>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
}
private void init() {
list = (ListViewDel) findViewById(R.id.list);
list.setRemoveListener(this);
for (int i = 0; i < 20; i++) {
dataSourceList.add((i > 2 ? "淘宝" + " " + i : "京东" + " 0" + i));
}
adapter = new ListItemAdapter(this);
adapter.setData(dataSourceList);
list.setAdapter(adapter);
list.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
Intent intent = new Intent(getApplicationContext(),
ListItemActivity.class);
intent.putExtra("item", dataSourceList.get(position));
startActivity(intent);
overridePendingTransition(R.anim.slide_in_from_right,
R.anim.remain_original_location);
}
});
}
public void removeItem(int position) {
ListViewDel.isSlide = false;
ListViewDel.itemView.findViewById(R.id.tv_coating).setVisibility(
View.VISIBLE);
dataSourceList.remove(position);
adapter.notifyDataSetChanged();
}
}
(本篇完)