最近在做即时通讯这一块,老板不知道怎么想的,反正完全是照着QQ做的,而我个人又非常喜欢qq侧滑操作这个效果,于是想找来用,但是翻遍了网络,没有一个像样的例子,有相似的也是各种bug各种限制,我也崩溃啊,没办法只好自己搞一个了。不多说上效果图:
具体实现呢,是自定义的listview来做的,本来的思路是自定义item的根布局来做,结果出来之后,事件的分发处理,特别不好弄,各种不流畅。
于是改用自定义listview,结果很喜人,个人觉得比较完美,支持3.0以下,
由于是属性动画需要支持到3.0以下,所以使用了nineoldandroid这个开源的属性动画库。
下面看代码:
public class InfoListView extends ListView {
private int mScreenWidth; // 屏幕宽度
private float mDownX; // 按下点的x值
private float mDownY; // 按下点的y值
private int mActionViewWidth;// 操作view的宽度
/**
* 执行动画的时间
*/
protected long mAnimationTime = 150;
private boolean isActionViewShow = false; // 删除按钮是否正在显示
private ViewGroup mPointChild; // 当前处理的item
private LinearLayout.LayoutParams mLayoutParams; // 当前处理的item的LayoutParams
private int touchSlop;//最小偏移量超过这个值才处理滑动事件
private float scroll;//偏移的距离
private int openedIntemPosition = -1;//记录已经打开的item的位置
private int childPosition;//手指落下位置的item的position
private boolean iswiping = false;//手指是否正在滑动
private boolean isDownToNormal = false;//判断之前是否是手指落下导致消失。
private boolean isUPToNormal = false;//判断之前是否是手指离开导致消失。
private long lastTime;//计算手指两次的触摸间隔
public InfoListView(Context context) {
this(context, null);
}
public InfoListView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public InfoListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// 获取屏幕宽度
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics dm = new DisplayMetrics();
wm.getDefaultDisplay().getMetrics(dm);
mScreenWidth = dm.widthPixels;
final ViewConfiguration configuration = ViewConfiguration.get(getContext());
touchSlop = configuration.getScaledTouchSlop();
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
performActionDown(ev);
break;
case MotionEvent.ACTION_MOVE:
return performActionMove(ev);
case MotionEvent.ACTION_UP:
performActionUp(ev);
break;
}
return super.onTouchEvent(ev);
}
// 处理action_down事件
private void performActionDown(MotionEvent ev) {
//如果触摸间隔小于100MS 不让进来~
if (System.currentTimeMillis() - lastTime <= mAnimationTime) {
lastTime = System.currentTimeMillis();
return;
}
lastTime = System.currentTimeMillis();
mDownX = ev.getX();
mDownY = ev.getY();
if (isActionViewShow) {
childPosition = pointToPosition((int) mDownX, (int) mDownY);
if (childPosition == AdapterView.INVALID_POSITION) {
return;
}
if ((openedIntemPosition != childPosition - getFirstVisiblePosition())) {
//全部恢复初始值不作响应
turnToNormal();
isDownToNormal = true;
mDownX = -1;
mDownY = -1;
return;
}
}
if ((!isActionViewShow && openedIntemPosition == -1)) {
// 获取当前点的item
childPosition = pointToPosition((int) mDownX, (int) mDownY);
if (childPosition == AdapterView.INVALID_POSITION) {
return;
}
openedIntemPosition = childPosition - getFirstVisiblePosition();
mPointChild = (ViewGroup) getChildAt(openedIntemPosition);
// 获取操作view宽度
mActionViewWidth = mPointChild.getChildAt(1).getLayoutParams().width;
mLayoutParams = (LinearLayout.LayoutParams) mPointChild.getChildAt(0)
.getLayoutParams();
// 为什么要重新设置layout_width 等于屏幕宽度
// 因为match_parent时,不管你怎么滑,都不会显示删除按钮
// why? 因为match_parent时,ViewGroup就不去布局剩下的view
mLayoutParams.width = mScreenWidth;
mPointChild.getChildAt(0).setLayoutParams(mLayoutParams);
}
}
// 处理action_move事件
private boolean performActionMove(MotionEvent ev) {
if ((Math.abs(ev.getX() - mDownX) > touchSlop
&& Math.abs(ev.getY() - mDownY) < touchSlop)
|| (Math.abs(ev.getX() - mDownX) > touchSlop
&& Math.abs(ev.getY() - mDownY) > touchSlop
&& (Math.abs(ev.getX() - mDownX) > Math.abs(ev.getY() - mDownY)))) {
iswiping = true;
//当手指滑动item,取消item的点击事件,不然我们滑动Item也伴随着item点击事件的发生
MotionEvent cancelEvent = MotionEvent.obtain(ev);
cancelEvent.setAction(MotionEvent.ACTION_CANCEL |
(ev.getActionIndex() << MotionEvent.ACTION_POINTER_INDEX_SHIFT));
onTouchEvent(cancelEvent);
}
if (isActionViewShow) {
childPosition = pointToPosition((int) mDownX, (int) mDownY);
if (childPosition == AdapterView.INVALID_POSITION) {
return true;
}
if ((openedIntemPosition != childPosition - getFirstVisiblePosition())) {
//全部恢复初始值不作响应
turnToNormal();
isDownToNormal = true;
mDownX = -1;
mDownY = -1;
iswiping = false;
return true;
}
}
if (iswiping) {
scroll = ev.getX() - mDownX;
if (Math.abs(scroll) <= mActionViewWidth) {
if ((scroll <= 0 && !isActionViewShow)) {
ViewHelper.setTranslationX(mPointChild.getChildAt(0), scroll);
ViewHelper.setTranslationX(mPointChild.getChildAt(1), scroll);
} else if ((scroll >= 0 && isActionViewShow && openedIntemPosition != -1)) {
ViewHelper.setTranslationX(mPointChild.getChildAt(0), (scroll - mActionViewWidth));
ViewHelper.setTranslationX(mPointChild.getChildAt(1), (scroll - mActionViewWidth));
}
}
return true;
}
return super.onTouchEvent(ev);
}
// 处理action_up事件
private void performActionUp(MotionEvent ev) {
if (!iswiping) {
if (isActionViewShow && openedIntemPosition != -1) {
isUPToNormal = true;
turnToNormal();
} else {
turnToNormal();
}
return;
}
scroll = ev.getX() - mDownX;
// 偏移量大于操作view的一半,则显示
// 否则恢复默认
//向左滑动
if (scroll < 0) {
if ((-scroll >= mActionViewWidth / 4) && !isActionViewShow) {
show();
} else if ((-scroll < mActionViewWidth / 4) || ((-scroll >= mActionViewWidth / 4) && isActionViewShow)) {
if (isActionViewShow) {
childPosition = pointToPosition((int) mDownX, (int) mDownY);
if (childPosition == AdapterView.INVALID_POSITION) {
return;
}
if (openedIntemPosition == childPosition - getFirstVisiblePosition()) {
return;
}
}
turnToNormal();
}
} else if (scroll > 0) {//向右滑动
if (scroll < mActionViewWidth / 4 && isActionViewShow && openedIntemPosition != -1) {
show();
} else if (scroll >= mActionViewWidth / 4 && isActionViewShow && openedIntemPosition != -1) {
turnToNormal();
} else {
//如果没有展开的其它情况全部恢复原样
turnToNormal();
}
}
iswiping = false;
}
@Override
public boolean performItemClick(View view, int position, long id) {
//当有展开的item的时候,不响应item的点击事件
if (isActionViewShow || isDownToNormal || isUPToNormal) {
isDownToNormal = false;
isUPToNormal = false;
if (isActionViewShow && openedIntemPosition != -1) {
turnToNormal();
}
return false;
}
return super.performItemClick(view, position, id);
}
/**
* 隐藏操作view
*/
public void turnToNormal() {
if (mPointChild == null) {
return;
}
isActionViewShow = false;
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(mPointChild.getChildAt(0), "translationX", 0);
ObjectAnimator objectAnimator1 = ObjectAnimator.ofFloat(mPointChild.getChildAt(1),
"translationX", mActionViewWidth);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(objectAnimator, objectAnimator1);
animatorSet.setDuration(mAnimationTime);
animatorSet.start();
animatorSet.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
openedIntemPosition = -1;
}
});
}
/**
* 显示操作view
*/
private void show() {
if (mPointChild == null) {
return;
}
isActionViewShow = true;
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(mPointChild.getChildAt(0), "translationX",
-mActionViewWidth);
ObjectAnimator objectAnimator1 = ObjectAnimator.ofFloat(mPointChild.getChildAt(1), "translationX",
-mActionViewWidth);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(objectAnimator, objectAnimator1);
animatorSet.setDuration(mAnimationTime);
animatorSet.start();
}
/**
* 是否显示
* @return
*/
public boolean isActionViewShow() {
return isActionViewShow;
}
}
这里面需要注意的是item的布局:
<?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:background="@android:color/white"
android:gravity="center_vertical"
android:orientation="horizontal">
<!--初显示界面-->
<LinearLayout
android:id="@+id/id_front"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white"
android:paddingBottom="10dp"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:paddingTop="10dp"
android:orientation="horizontal">
<ImageView
android:id="@+id/infoImage"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_centerVertical="true"
android:scaleType="centerCrop"
android:src="@android:drawable/star_on" />
<LinearLayout
android:layout_weight="3.0"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_centerVertical="true"
android:layout_marginLeft="10dp"
android:layout_toLeftOf="@+id/infoTimeAndCount"
android:layout_toRightOf="@+id/infoImage"
android:gravity="center_vertical"
android:orientation="vertical">
<TextView
android:id="@+id/infoTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Jay" />
<TextView
android:textSize="13sp"
android:id="@+id/infoLast"
android:textColor="@android:color/darker_gray"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text="什么事啊??" />
</LinearLayout>
<LinearLayout
android:layout_weight="1.0"
android:id="@+id/infoTimeAndCount"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:gravity="center_vertical|right"
android:orientation="vertical">
<TextView
android:id="@+id/infoTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Jay" />
<TextView
android:id="@+id/infoCount"
android:textSize="13sp"
android:textColor="@android:color/white"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:gravity="center"
android:text="99+"
/>
</LinearLayout>
</LinearLayout>
<!--滑动显示界面-->
<LinearLayout
android:id="@+id/id_back"
android:layout_width="160dp"
android:layout_height="70dp"
android:background="@android:color/white"
android:gravity="center|right"
android:tag="id_back">
<TextView
android:id="@+id/id_putTop"
android:textSize="19sp"
android:textColor="@android:color/white"
android:layout_width="80dp"
android:layout_height="match_parent"
android:background="@android:color/darker_gray"
android:clickable="true"
android:gravity="center"
android:text="置顶" />
<TextView
android:id="@+id/id_delete"
android:textSize="19sp"
android:textColor="@android:color/white"
android:layout_width="80dp"
android:layout_height="match_parent"
android:background="@color/red"
android:clickable="true"
android:gravity="center"
android:text="删除" />
</LinearLayout>
</LinearLayout>
以上就是关键代码了,大家可以直接拿来用,附上源码下载地址:~
完美仿QQ消息界面功能
程序员内功修炼手册 不定期分享程序员基础知识,大前端知识!想跟博主一块成长的快快关注吧!