Android 实现新闻端平拉动删除,拉下条新闻,上条新闻弹回特效

一、概述:

1、目前一般的手机下拉信息栏时,会显示很多自己不想看到的内能,但我们又不想去触发它,这些厂商就实现了平拉动的时候可以把这些信息删除,就不会显示在顶部的信息里了!
2、但我们今天要实现的效果与这个差不多,但必须手动点击删除,如图:
这里写图片描述

3、那我们如何实现呢?仔细观看图片,其实是使用了两个图层,上面是个拖拽图层,下面是个按钮图层,把上面一层移动到某个位置时,显示下面的按钮,然后点击按钮,隐藏上面的图层
那既然讲到了侧拉,当然会想到现在最流行的工具类ViewDragHelper
那么我们先实现基本的功能。。。。。。。。。。。

二、实现基本功能:

1、创建自定义的View

public class SwipeLayout extends FrameLayout {
    public SwipeLayout(Context context) {
        super(context, null);
    }

    public SwipeLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public SwipeLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

}

2、创建布局

<?xml version="1.0" encoding="utf-8"?>
<com.android.imooc.swipelayout.SwipeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >


</com.android.imooc.swipelayout.SwipeLayout>

3、创建主页

public class SwipeActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_swipe);
    }
}

这些基础框架搭建好后,我们得开始写ViewDragHelper。。。。。。。。。。。

三、ViewDragHelper基本写法

1、第一步:在构造方法里初始化

mHelper = ViewDragHelper.create(this, 1.0f, mCallback);

private Callback mCallback = new Callback() {
    @Override
    public boolean tryCaptureView(View arg0, int arg1) {
        // TODO Auto-generated method stub
        return false;
    }
};

2、第二步:复写onInterceptTouchEvent

@Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return mHelper.shouldInterceptTouchEvent(ev);
    }

3、第三步:复写onTouchEvent

@Override
    public boolean onTouchEvent(MotionEvent event) {
        try {
            mHelper.processTouchEvent(event);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return true;
    }

四、实现基本的拖动效果

1、我们现在写个有两层的布局文件,上层是内容,下面一层是按钮

<?xml version="1.0" encoding="utf-8"?>
<com.android.imooc.swipelayout.SwipeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/sl"
    android:layout_width="match_parent"
    android:layout_height="60dp"
    android:background="#44000000"
    android:minHeight="60dp" >

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:orientation="horizontal" >

        <TextView
            android:id="@+id/tv_call"
            android:layout_width="60dp"
            android:layout_height="match_parent"
            android:background="#666666"
            android:gravity="center"
            android:text="Call"
            android:textColor="#ffffff" />

        <TextView
            android:id="@+id/tv_del"
            android:layout_width="60dp"
            android:layout_height="match_parent"
            android:background="#ff0000"
            android:gravity="center"
            android:text="Delete"
            android:textColor="#ffffff" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#44ffffff"
        android:gravity="center_vertical"
        android:orientation="horizontal" >

        <ImageView
            android:id="@+id/iv_image"
            android:layout_width="40dp"
            android:layout_height="40dp"
            android:layout_marginLeft="15dp"
            android:src="@drawable/head_1" />

        <TextView
            android:id="@+id/tv_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="15dp"
            android:text="Name" />
    </LinearLayout>

</com.android.imooc.swipelayout.SwipeLayout>

2、在SwipeLayout里的回调函数里复写clampViewPositionHorizontal方法

@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
    return left;
}

3)在onFinishInflate方法里获得子控件

private View mBackView;
private View mFrontView;
/**
     * 当xml被填充完成后调用
     */
    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        mBackView = getChildAt(0);
        mFrontView = getChildAt(1);
    }

4、在onLayout里来设置子控件的位置

@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    super.onLayout(changed, left, top, right, bottom);
    //布局子控件
    layoutChild(false);
}

/**
 * 根据控件是否关闭来布
 * @param isOpen
 */
private void layoutChild(boolean isOpen) {
    Rect frontRect = computeFrontViewRect(isOpen);
    mFrontView.layout(frontRect.left, frontRect.top, frontRect.right, frontRect.bottom);
    Rect backRect = computeBackViewRect(frontRect);
    mBackView.layout(backRect.left, backRect.top, backRect.right, backRect.bottom);

    //绘制完后置于前面
    bringChildToFront(mFrontView);
}

private Rect computeBackViewRect(Rect frontRect) {
    int left = frontRect.right;
    return new Rect(left, frontRect.top, left + mRange,  frontRect.bottom);
}

private Rect computeFrontViewRect(boolean isOpen) {
    int left = 0;
    int top = 0;
    int right = left + mWidth;
    int bottom = top + mHeight;
    if (isOpen) {
        left = -mRange;
    }
    return new Rect(left, top, right, bottom);
}


@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);
    mWidth = mFrontView.getMeasuredWidth();
    mHeight = mFrontView.getMeasuredHeight();

    mRange = mBackView.getMeasuredWidth();
}

5.如何实现上层控件拖动下层也跟着拖动,在回调函数里复写onViewPositionChanged方法
@Override

public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
    if (changedView == mFrontView) {
        //让后面的view跟着前面的view相同的距离
        mBackView.offsetLeftAndRight(dx);               
    }else if (changedView == mBackView) {
        mFrontView.offsetLeftAndRight(dx);
    }

    if (dx > mRange) {
        dx = mRange;
    }else if (dx < 0) {
        dx = 0;
    }

    invalidate();
}

6、限制移动的范围

/**
 * 在这里修正
 */
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
    if (child == mFrontView) {
        if (left < -mRange) {
            left = -mRange;
        } else if (left > 0) {
            left = 0;
        }
    } else if (child == mBackView) {
        if (left < mWidth - mRange) {
            left = mWidth - mRange;
        } else if (left > mWidth) {
            left = mWidth;
        }
    }
    return left;
}

7、在松手的时候,实现如果>mrang/2时就open,否则就close

@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
    super.onViewReleased(releasedChild, xvel, yvel);
    if (xvel == 0 && mFrontView.getLeft() < -mRange / 2.0f) {
        open();
    } else if (xvel < 0) {
        open();
    } else {
        close();
    }
}

8、但这样是不是太单调了,没有动画效果,如果你的boss发现,岂不要被叼,那我们就加上一个平滑的效果

public void open(boolean isSmooth){
        int finalLeft = -mRange;
        if (isSmooth) {
            //开始动画
            if (mHelper.smoothSlideViewTo(mFrontView, finalLeft, 0)) {
                //没到达重绘
                ViewCompat.postInvalidateOnAnimation(this);
            }
        }else {
            open();
        }
    }

    protected void open() {
        layoutChild(true);
    }

    protected void close() {
        layoutChild(false);
    }

    public void close(boolean isSmooth){
        int finalLeft = 0;
        if (isSmooth) {
            //开始动画
            if (mHelper.smoothSlideViewTo(mFrontView, finalLeft, 0)) {
                //没到达重绘
                ViewCompat.postInvalidateOnAnimation(this);
            }
        }else {
            close();
        }
    }

    /**
     * 实现持续动画
     */
    @Override
    public void computeScroll() {
        super.computeScroll();
        if (mHelper.continueSettling(true)) {
            ViewCompat.postInvalidateOnAnimation(this);
        }
    }

五、添加状态与监听

1.添加状态

private Status msStatus = Status.CLOSE;
public static enum Status {
        CLOSE, OPEN, DRAGING
    }

2.添加监听

public interface onSwipeListener{
    void onClose(SwipeLayout layout);
    void onOpen(SwipeLayout layout);
    void onDraging(SwipeLayout layout);
    void onStartOpen(SwipeLayout layout);//表示准备打开时,其它的条目开始关闭
    void onStartClose(SwipeLayout layout);
}
private onSwipeListener mListener;
public void setOnSwipeListener(onSwipeListener listener){
    this.mListener = listener;
}

3.在什时候调用监听呢?当然是在位置变化的时候,所以我们在onViewPositionChanged方法里插入方法
dispatchSwipeEvent();

protected void dispatchSwipeEvent() {
    mListener.onDraging(this);
    // 记录上一次的状态
    Status preStatus = mStatus;
    // 更新当前状态
    mStatus = updateStatus();

    // 判断
    if (mStatus != preStatus && mListener != null) {
        if (mStatus == Status.CLOSE) {
            mListener.onClose(this);
        } else if (mStatus == Status.OPEN) {
            mListener.onOpen(this);
        } else if (mStatus == Status.DRAGING) {
            //如果上次是关闭状态,就表示准备打开
            if (preStatus == Status.CLOSE) {
                mListener.onStartOpen(this);
            }else if (preStatus == Status.OPEN) {
                mListener.onStartClose(this);
            }
        }
    }
}

/**
 * 更新状态
 * 
 * @return
 */
private Status updateStatus() {
    int left = mFrontView.getLeft();
    if (left == 0) {
        return Status.CLOSE;
    } else if (left == -mRange) {
        return Status.OPEN;
    }
    return Status.DRAGING;
}

六、把上面的item添加到listview

1.创建布局

<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"
    tools:context=".MainActivity" >

    <ListView 
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/lv"
        ></ListView>

</RelativeLayout>

2、把主页布局设置成上面的布局

setContentView(R.layout.activity_swipe_list);

3、把原来的布局改成item_list布局
4、在主页里初始化数据

private void initDatas() {
        mOpenLayouts = new HashMap<Integer, SwipeLayout>();
        mDatas = new ArrayList<String>();
        for (int i = 0; i < Cheeses.NAMES.length; i++) {
            mDatas.add(Cheeses.NAMES[i]);
        }

    }

5、创建Adapter

private QuickAdapter<String> mAdapter = new QuickAdapter<String>(this, R.layout.item_list, mDatas) {
        private SwipeLayout mSwipeLayout;

        @Override
        protected void convert(final BaseAdapterHelper helper, final String item) {
            //设置数据
            helper.setText(R.id.tv_name, item);
            notifyDataSetChanged();

            final int position  = helper.getPosition();
            Logger.i("tag", "position ==" + position);

            View call = helper.getView(R.id.tv_call);

            //删除
            View del = helper.getView(R.id.tv_del);
            del.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    remove(item);
                }
            });



            View convertView = helper.getView();


            //设置监听打开一个条目,关闭其它条目
            if (convertView instanceof SwipeLayout) {
                mSwipeLayout = (SwipeLayout) convertView;

                mSwipeLayout.setOnSwipeListener(new onSwipeListener() {
                    @Override
                    public void onStartOpen(SwipeLayout layout) {
                        Logger.d(TAG, "onStartOpen");
                        for(Integer key : mOpenLayouts.keySet()){
                            SwipeLayout swipeLayout = mOpenLayouts.get(key);
                            swipeLayout.close(true);
                        }
                        mOpenLayouts.clear();
                    }

                    @Override
                    public void onStartClose(SwipeLayout layout) {
                        Logger.d(TAG, "onStartClose");

                    }

                    @Override
                    public void onOpen(SwipeLayout layout) {
                        Logger.d(TAG, "onOpen");
                        //添加到集合
                        mOpenLayouts.put(position, layout);
                    }

                    @Override
                    public void onDraging(SwipeLayout layout) {
                        Logger.d(TAG, "onDraging");
                    }

                    @Override
                    public void onClose(SwipeLayout layout) {
                        Logger.d(TAG, "onClose");
                        mOpenLayouts.remove(position);
                    }
                });
            }

        }
    };

6、把adapter设置到listview里,好了,这样就大功搞成了

七、源码下载:

链接:http://pan.baidu.com/s/1c1Krxpm 密码:u5ys

———————————————————————
有需求者请加qq:136137465,非诚勿扰
(java 架构师全套教程,共760G, 让你从零到架构师,每月轻松拿3万)
01.高级架构师四十二个阶段高
02.Java高级系统培训架构课程148课时
03.Java高级互联网架构师课程
04.Java互联网架构Netty、Nio、Mina等-视频教程
05.Java高级架构设计2016整理-视频教程
06.架构师基础、高级片
07.Java架构师必修linux运维系列课程
08.Java高级系统培训架构课程116课时
(送:hadoop系列教程,java设计模式与数据结构, Spring Cloud微服务, SpringBoot入门)
——————————————————————–

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lovoo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值