Android右划喜欢左划不喜欢的View

最近,额,不对,挺久之前,有一类交友APP,喜欢的右划,不喜欢的左划。看到的时候感觉还挺新鲜的,就琢磨着自己整一波。下面上图,那阴影应该是录制软件的关系,连鼠标印子都上去了。

like_or_not_1.gif
like_or_not_2.gif

差不多就这效果
https://github.com/Linyuzai/Demo4LikeOrNotView


然后这个控件叫LikeOrNotView(真是佩服自己取出这么接地气的名字=。=)
然后用法。

<com.linyuzai.likeornot.LikeOrNotView xmlns:like_or_not="http://schemas.android.com/apk/res-auto"    
    android:id="@+id/like_or_not"    
    android:layout_width="match_parent"           
    android:layout_height="match_parent" 
    android:background="#eeeeee"    
    like_or_not:animator_duration="300"    
    like_or_not:drag_scale="0.35"    
    like_or_not:move_multiplier="3"    
    like_or_not:rotation_direction="clockwise"    
    like_or_not:rotation_range="5"    
    like_or_not:style_rotatable="true"    
    like_or_not:style_stratified="true">    
    <LinearLayout        
        android:layout_width="match_parent"        
        android:layout_height="match_parent"        
        android:layout_marginTop="@dimen/activity_horizontal_margin"        
        android:gravity="center"        
        android:orientation="horizontal">        
    <ImageView            
        android:id="@+id/back"            
        android:layout_width="50dp"            
        android:layout_height="50dp"            
        android:layout_marginRight="40dp"            
        android:layout_marginTop="20dp"            
        android:scaleType="centerInside"            
        android:src="@mipmap/back" />        
    <ImageView            
        android:id="@+id/nope"            
        android:layout_width="50dp"            
        android:layout_height="50dp"            
        android:layout_marginRight="40dp"            
        android:scaleType="centerInside"            
        android:src="@mipmap/nope" />        
    <ImageView            
        android:id="@+id/like"            
        android:layout_width="50dp"            
        android:layout_height="50dp"            
        android:layout_marginRight="40dp"            
        android:scaleType="centerInside"            
        android:src="@mipmap/like" />        
    <ImageView            
        android:id="@+id/star"            
        android:layout_width="50dp"            
        android:layout_height="50dp"            
        android:layout_marginTop="20dp"            
        android:scaleType="centerInside"            
        android:src="@mipmap/star" />    
    </LinearLayout>
</com.linyuzai.likeornot.LikeOrNotView>

里面需要一个子控件,就是图中下面四个按钮的那块。如果不需要那块东西,就随便放个view上去,因为我默认是有一个子view。说明一下那个,自定义的属性

Attrs Introduce
style_rotatable 拖动的时候可旋转
style_stratified 是否有层次效果
rotation_direction 旋转方向
默认右划顺时针
rotation_range 旋转范围
会乘一定比例,默认5
animator_duration 非手动拖动时动画持续时间
默认300ms
drag_scale 设置有效滑动距离
为屏幕宽度*drag_scale,默认0.35
move_multiplier 松手后view滑动距离和用手滑动距离的倍数
说简单点,当你发现手一甩,view没甩出屏幕
就把这个参数加大

然后设置一下Adapter(和Listener)

mLikeOrNotView.setAdapter(new BaseAdapter() {    
    @Override    
    public int getCount() {        
        return 10;    
    }    

    @Override    
    public View getView(View convertView, ViewGroup parent, final int position) {        
        if (convertView == null)          
            convertView=LayoutInflater.from(parent.getContext()).inflate(R.layout.item_like_or_not, parent, false);        
        TextView textView = (TextView) convertView.findViewById(R.id.text);        
        textView.setText(position + "");              
        return convertView;    
    }
});

mLikeOrNotView.setOnItemClickListener(new LikeOrNotView.OnItemClickListener() {    
    @Override    
    public void onItemClick(View view, int position) {   
     
    }
});

mLikeOrNotView.setOnLikeOrNotListener(new LikeOrNotView.OnLikeOrNotListener() {    
    @Override    
    public void onLike(View view, int position) {        
        
    }    

    @Override    
    public void onNope(View view, int position) {        
        
    }    

    @Override    
    public void onAnimationEnd() {        
        
    }
});

mLikeOrNotView.setViewStateCallback(new LikeOrNotView.ViewStateCallback() {    
    @Override    
    public void onViewReleased(View view) {        
  
    }    
    @Override    
    public void onViewPositionChanged(View view, int left, int top, float scale) {        

    }
});

主要就是用ViewDragHelper和ObjectAnimator,说实话一直觉得ViewDragHelper有bug,所以平时都喜欢直接写onTouchEvent,虽然ViewDragHelper确实很方便,然后就讲一下思路,和一些坑(如果我还记得,省略部分代码。。。)

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {    
    int mOffsetY = 0;    
    switch (ev.getAction()) {        
        case MotionEvent.ACTION_DOWN:            
            mDownX = ev.getX();            
            mDownY = ev.getY();            
            mOffsetY = (int) ev.getY();            
        break;    
    }    
    return mOffsetY > mLimitBottomY ? super.onInterceptTouchEvent(ev) : mDragHelper.shouldInterceptTouchEvent(ev);
}

mLimitBottomY可滑动范围的最大Y值,超过了就交给父类的onInterceptTouchEvent方法,没超过就交给ViewDragHelper的shouldInterceptTouchEvent方法。另外,当你的view可以点击时,在onTouchEvent里面拿不到MotionEvent.ACTION_DOWN,所以在onInterceptTouchEvent里面那手指按下的坐标。

@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {          
    if (Math.abs(mDownX - mMoveX) < mLimitDragDistance) {        
        //滑动距离不够,最终位置为初始位置       
        mFinalPosition.x = mInitialPosition.x;        
        mFinalPosition.y = mInitialPosition.y;    
    } else {        
        //最终的位置为手滑动的距离*mMoveMultiplier        
        mFinalPosition.x = mInitialPosition.x + (int) (mMoveX - mDownX) * mMoveMultiplier;        
        mFinalPosition.y = mInitialPosition.y + (int) (mMoveY - mDownY) * mMoveMultiplier;        
        //如果有层次,将下面两张按比例还原        
        if (isStratified)            
            overlapAfterReleased();    
    }    
    mDragHelper.settleCapturedViewAt(mFinalPosition.x, mFinalPosition.y);    
    invalidate();    
}

手松开时,滑动距离不够的话(滑动距离可以用drag_scale调整,默认屏幕宽度的0.35)回到原来的位置,要不然就把view甩出去,坐标为手按下的坐标减掉手松开的坐标乘一个倍数(可用move_multiplier设置)

@Override
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {    
    float scale = (left - mInitialPosition.x) / mLimitDragDistance;    
    //如果可旋转,旋转一定角度    
    if (mRotatable) {        
        float mRotation = 0f;        
        switch (mRotationDirection) {            
            case ANTICLOCKWISE:                
                mRotation = -mRotationRange;                
            break;            
            case CLOCKWISE:                
                mRotation = mRotationRange;                
            break;        
        }        
        //旋转        
        changedView.setRotation(mRotation * scale);    
    }    
    //返回原处或被甩出去    
    if (left == mFinalPosition.x && top == mFinalPosition.y) {        
        //设置为没有旋转角度        
        changedView.setRotation(0f);        
        //被甩出去        
        if (mReleasedView != null && left != mInitialPosition.x && top != mInitialPosition.y) {            
            mReleasedView = null;            
            //当前的position+1            
            mCurrentPosition++;            
            removeView(changedView);            
            //在最后载入一个view,如果需要有层次,进行层次的动画              
            setStratifiedAfterLoadView(loadView(changedView));            
        }
    }
}

如果要旋转,就按照某个比例旋转,当位置和甩出的位置一样,就把这个view给remove,然后在最后添加一个view。这边讲一下view的回收,我的思路是这样的,先加载5个view,然后每次划走一个,就把这个划走的view当做convertView传回去,加载再后面的那一个,形成一个循环。如果view都加载完了,多出来的view存到一个list里面,以备后用。然后是层次效果

private void initOverlap() {    
    View child0 = getChildAt(0);    
    View child1 = getChildAt(getChildCount() - 2);    
    View child2 = getChildAt(getChildCount() - 3);    
    if (child1 != null && child1 != child0) {        
        child1.setScaleX(1f - mScale);        
        child1.setScaleY(1f - mScale);          
        child1.setTranslationY(mOffsetY);    
    }    
    if (child2 != null && child2 != child0) {        
        child2.setScaleX(1f - 2f * mScale);        
        child2.setScaleY(1f - 2f * mScale);        
        child2.setTranslationY(2f * mOffsetY);    
    }    
    if (getChildCount() > 4) {        
        for (int index = 1; index < getChildCount() - 3; index++) {            
            View child = getChildAt(index);            
            if (child != null) {                
                child.setScaleX(1f - 2f * mScale);                
                child.setScaleY(1f - 2f * mScale);                  
                child.setTranslationY(2f * mOffsetY);            
            }        
        }    
    }
}

这是层次效果的初始化,拿到最顶上第二个view和的三个view,通过setScale,setTranslation进行变换(就是从ObjectAnimator受到的启发,记得变换完了以后,不管是用动画还是直接设置,都需要再手动设置回来)


其实这样讲下来,好像也没多么坎坷,不过其实里面还是有有一些bug我还是弄不太明白,我之所以一开始加载5个view而不是3个就是因为那个bug。嗯,差不多就是这样,想到其他的再补。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,491评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,856评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,745评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,196评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,073评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,112评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,531评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,215评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,485评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,578评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,356评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,215评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,583评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,898评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,497评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,697评论 2 335

推荐阅读更多精彩内容