zoukankan      html  css  js  c++  java
  • Android开发学习之路-PopupWindow和仿QQ左滑删除

    这周作业,要做一个类似QQ的左滑删除效果的ListView,因为不想给每个item都放一个按钮,所以决定用PopupWindow,这里记录一下

    先放一下效果图:

    先说明一下这里面的问题:

    ①没有做到像QQ那样可以允许item跟随手指移动,虽然PopupWindow有update方法让我们动态移动,但是在屏幕外移动会没有动画效果,直接弹进来

    ②仔细观察可以发现,item的滑动和删除按钮的滑动是分开的,无法保证它们会一起播放,QQ的动画可以

    再说说大概的思路,因为我们没有让item都带上Button,所以用到了PopupWindow来做删除按钮,我们可以通过自定义ListView来监听手势,如果判断了是左滑动,则让PopupWindow动态显示出来,在让item做一个平移的效果,当两者的持续时间一致的时候,效果就出来了。接下来,如果用户点击了PopupWindow以外的区域,PopupWindow会消失,回调dismiss方法,我们再把item移回原位。

    介绍一下PopupWindow,它允许我们在屏幕上的任意位置弹出一个提示框,提示框的布局我们可以自己定义,我们上面的删除按钮,其实就是一个PopupWindow。

    如果我们要使用PopupWindow,可以像下面这样,定义一个自定义布局的PopupWindow

    View view = LayoutInflater.from(context).inflate(R.layout.item_delete, null);
    PopupWindow mPopupWindow = new PopupWindow(view, WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT, true);

    如果我们要显示这个PopupWindow,可以调用showAtLocation或者showAsDropDown,前者是直接把PopupWindow显示在某个特定位置,后者是在某个位置从上向下弹出,这里我们调用第一个方法,有兴趣的可以尝试下第二个,参数什么的可以不用管,不懂的看看API就知道了,之后再调用update方法更新位置信息

    mPopupWindow.showAtLocation(itemView, Gravity.LEFT | Gravity.TOP, locations[0]+ itemView.getWidth() - popWidth, locations[1]);
    mPopupWindow.update();

    至于对PopupWindow设置背景什么的,需要的时候看看API就知道了!这里不详说。

    至于效果的实现,我们一步一步的来。

    ①自定义一个ListView,详细的做法,已经在下面做了注释

    public class SlidableListView extends ListView {
        private static final String TAG = "SlidableListView";
        private int touchSlop;
    
        private View itemView; // 每个Item的View
        private int mCurrentDownPosition; // 当前点击的位置,用来找出点击的Item
        private PopupWindow mPopupWindow; // 删除按钮
        private int popHeight, popWidth; // 删除按钮的高和宽
        private boolean isSliding = false; // 手指是否在向左滑动
        private int downX, downY, moveX, moveY; // 点击的X和Y,以及移动的X和Y
        private OnItemDeleteListener onItemDeleteListener; // 点击删除按钮时候的回调接口
    
        public SlidableListView(Context context) {
            super(context);
        }
    
        // 在布局中使用自定义控件调用的构造函数,这里进行一些必要的初始化
        public SlidableListView(Context context, AttributeSet attrs) {
            super(context, attrs);
            // 初始化删除按钮,记得让按钮可以接收焦点
            View view = LayoutInflater.from(context).inflate(R.layout.item_delete, null);
            mPopupWindow = new PopupWindow(view, WindowManager.LayoutParams.WRAP_CONTENT,
                    WindowManager.LayoutParams.WRAP_CONTENT, true);
            // 获取删除按钮的宽和高
            view.measure(0, 0);
            popWidth = view.getMeasuredWidth();
            popHeight = view.getMeasuredHeight();
            // 获取滑动的最小距离
            touchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
        }
    
        public SlidableListView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
        }
    
        // 先拦截touch事件,根据不同情况进行分发
        @Override
        public boolean dispatchTouchEvent(MotionEvent ev) {
            int action = ev.getAction();
            int x = (int) ev.getX(), y = (int) ev.getY();
            switch (action) {
                case MotionEvent.ACTION_DOWN:
                    downX = x;
                    downY = y;
                    // 如果删除按钮正在显示,那么不处理这个触摸事件,并把按钮隐藏起来
                    if (mPopupWindow.isShowing()) {
                        mPopupWindow.dismiss();
                        return false; // 阻止事件传递
                    }
                    mCurrentDownPosition = pointToPosition(downX, downY);
                    // 获取触摸的item
                    itemView = getChildAt(mCurrentDownPosition - getFirstVisiblePosition());
                    break;
                case MotionEvent.ACTION_MOVE:
                    moveX = x;
                    moveY = y;
                    int deltaX = moveX - downX;
                    int deltaY = moveY - downY;
                    // 当判断为左滑的时候,把事件交给OnTouchEvent来处理
                    if (moveX < downX && Math.abs(deltaX) > touchSlop && Math.abs(deltaY) <
                            touchSlop) {
                        isSliding = true;
                    }
                    break;
            }
            return super.dispatchTouchEvent(ev);
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent ev) {
            int action = ev.getAction();
            // 判断是否已经有左右滑动
            if (isSliding) {
                switch (action) {
                    case MotionEvent.ACTION_UP:
                        isSliding = false;
                        break;
                    case MotionEvent.ACTION_MOVE:
                        // 获取item在屏幕中的位置信息
                        int[] locations = new int[2];
                        itemView.getLocationOnScreen(locations);
                        // 给删除按钮设置动画,这里自定了出现从右到左平移,隐藏从左到右
                        mPopupWindow.setAnimationStyle(R.style.PopupWindowAnimations);
                        // 设置删除按钮的位置,这里设置在item的最右端
                        mPopupWindow.showAtLocation(itemView, Gravity.LEFT | Gravity.TOP, locations[0]
                                + itemView.getWidth() - popWidth, locations[1]);
                        mPopupWindow.update();
                        // 给item播放平移动画,配合删除按钮
                        ObjectAnimator.ofFloat(itemView, "translationX", 0, -popWidth).setDuration
                                (200).start();
                        // 获取删除按钮的实例,这里是一个TextView
                        TextView delete = (TextView) mPopupWindow.getContentView().findViewById(R.id
                                .delete);
                        // 设置监听,如果删除按钮消失(点击按钮外的地方,按下back键)回调
                        mPopupWindow.setOnDismissListener(new PopupWindow.OnDismissListener() {
                            @Override
                            public void onDismiss() {
                                // 把item移回原位置
                                ObjectAnimator.ofFloat(itemView, "translationX", -popWidth, 0)
                                        .setDuration(200).start();
                            }
                        });
                        // 给删除按钮设置点击事件
                        delete.setOnClickListener(new OnClickListener() {
                            @Override
                            public void onClick(View v) {
                                // 获取点击的item位置,并调用回调接口方法发送出去,隐藏删除按钮
                                int position = SlidableListView.this.getPositionForView(itemView);
                                onItemDeleteListener.onItemDelete(position);
                                mPopupWindow.dismiss();
                            }
                        });
                        break;
                }
                // 如果有在滑动,消化事件
                return true;
            }
            return super.onTouchEvent(ev);
        }
    
        public void setOnItemDeleteListener(OnItemDeleteListener onItemDeleteListener) {
            this.onItemDeleteListener = onItemDeleteListener;
        }
    
        // 回调删除事件的接口
        public interface OnItemDeleteListener {
            void onItemDelete(int position);
        }
    }

    ②自定义消失和进入的动画,因为系统中没有我们想要的平移进入

    style.xml:

    <style name="PopupWindowAnimations" parent="@android:style/Animation">
        <item name="android:windowEnterAnimation">@anim/delete_enter</item>
        <item name="android:windowExitAnimation">@anim/delete_exit</item>
    </style>

    anim/delete_enter.xml:

    <?xml version="1.0" encoding="utf-8"?>
    <set xmlns:android="http://schemas.android.com/apk/res/android">
        <translate
            android:interpolator="@android:anim/accelerate_decelerate_interpolator"
            android:fromXDelta="200"
            android:toXDelta="0"
            android:fromYDelta="0"
            android:toYDelta="0"
            android:duration="200"/>
    </set>

    anim/delete_exit.xml:

    <?xml version="1.0" encoding="utf-8"?>
    <set xmlns:android="http://schemas.android.com/apk/res/android">
        <translate
            android:fromXDelta="0"
            android:toXDelta="200"
            android:fromYDelta="0"
            android:toYDelta="0"
            android:duration="200"/>
    </set>

    ③自定义Adapter,这里不给出了,都懂

    ④在布局中使用自定义的ListView

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                  android:orientation="vertical"
                  android:layout_width="match_parent"
                  android:layout_height="match_parent">
    
        <sixthweek.fndroid.com.sixthweek.SlidableListView
            android:id="@+id/slidableListView"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    
    </LinearLayout>

    ⑤在Activity中使用adapter传入数据即可

    public class SlidableListViewActivity extends AppCompatActivity {
        private SlidableListView slidableListView;
        private MyAdapter adapter;
        private List<Map<String, String>> data;
    
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_slidablelistview);
            slidableListView = (SlidableListView) findViewById(R.id.slidableListView);
            data = new ArrayList<>();
            for (int i = 0; i < 20; i++) {
                Map<String, String> map = new HashMap();
                map.put("name", "QQ" + i);
                data.add(map);
            }
            adapter = new MyAdapter(this, data);
            slidableListView.setAdapter(adapter);
            // 实现回调接口,处理逻辑
            slidableListView.setOnItemDeleteListener(new SlidableListView.OnItemDeleteListener() {
                @Override
                public void onItemDelete(int position) {
                    data.remove(position);
                    adapter.notifyDataSetChanged();
                }
            });
        }
    }
  • 相关阅读:
    oracle创建表空间自增长和创建用户
    Cmd Markdown 简明语法手册
    Excel VBA修改不同文件簿sheet页名字
    常用JS(JQ)插件研究
    CSS颜色大全(转载)
    React框架学习
    不同浏览器中空格符的兼容问题
    VHDL----基础知识1
    串口通讯1---单片机
    Qt5 程序发布打包
  • 原文地址:https://www.cnblogs.com/Fndroid/p/5372751.html
Copyright © 2011-2022 走看看