仿網(wǎng)易新聞添加欄目和拖拽欄目效果(一)

今天偶然在apkbus上看到了以下欄目拖拽功能,我們也化繁為簡散休,一步一步來簡單實現(xiàn)划址。
第一步夺颤,實現(xiàn)拖拽胁勺;
第二步署穗,拖拽后的動畫;
通過這篇文章可以了解的內(nèi)容:
第一封恰,view的拖拽俭驮;
第二春贸,屬性動畫的執(zhí)行逸嘀;
第三崭倘,view的位置計算(各種相對位置,比如相對屏幕琅坡,相對父view等等)
第四榆俺,拖拽影像的產(chǎn)生茴晋。

introduce.gif

第一步實現(xiàn)拖拽

通過對其源碼的分析,實現(xiàn)拖拽的主要用法就是如下代碼:

private static final ClipData EMPTY_CLIP_DATA = ClipData.newPlainText("", "");
@Override
public boolean onLongClick(View v) {
    mTvDrag.startDrag(EMPTY_CLIP_DATA,new View.DragShadowBuilder(),mTvDrag,0);
    return false;
}

注意這里只是設(shè)置在某個事件后(比如這里長按事件)開始拖拽烁涌,這以后撮执,如果我們按著view進行拖拽的話二打,那么拖拽監(jiān)聽將可以監(jiān)控到掂榔,雖然此時view不能拖動。
那我們?nèi)绾卧O(shè)置監(jiān)聽呢穴豫?上代碼:

mTvDrag.setOnDragListener(this);

設(shè)置監(jiān)聽還有另外一種方式,就是調(diào)用了startDrag方法的view的父類中去實現(xiàn)onDragEvent()方法逼友,這樣也可以監(jiān)聽到view的拖拽精肃。
注意點:
誰負責(zé)監(jiān)聽,那么這個監(jiān)聽的view就是可拖拽的范圍帜乞,這個很重要司抱,因為這涉及到拖拽時坐標(biāo)的計算

@Override
public boolean onDragEvent(DragEvent event) {
    int action = event.getAction();
    // 拖拽點x和y坐標(biāo)
    int eventX = (int) event.getX();
    int eventY = (int) event.getY();

    switch (action) {
        // 拖拽開始監(jiān)聽
        case DragEvent.ACTION_DRAG_STARTED:
            break;
        // 拖拽進入時監(jiān)聽,可以開始進行拖拽
        // 和STARTED區(qū)別稍后講
        case DragEvent.ACTION_DRAG_ENTERED:
            Log.d("zp_test", "ENTERED " + event.getY());
            break;
        // 拖拽進行中
        case DragEvent.ACTION_DRAG_LOCATION:
            break;
        // 拖拽結(jié)束
        case DragEvent.ACTION_DRAG_ENDED:
        case DragEvent.ACTION_DRAG_EXITED:
            break;
        // 拖拽松開
        case DragEvent.ACTION_DROP:
            break;
    }
    return true;
}

也許你會講黎烈,直接用onTouchListener和layout方法也可以實現(xiàn)view跟隨手指一起滑動跋澳匀谣!沒錯是可以,但是用它來實現(xiàn)更為復(fù)雜的內(nèi)容時资溃,那將是場災(zāi)難武翎,比如長按后拖動溶锭,點擊事件不被屏蔽等等,實現(xiàn)起來就顯得稍微麻煩了露久。

第二步繪制影像陰影

細心的你一定能夠發(fā)現(xiàn)迟几,在拖拽的時候,拖拽的view是透明度比本身的view要低的一個影像缸逃。拖拽實現(xiàn)的原理其實是:在可拖拽區(qū)域放置一個framelayout昭殉,在framelayout中有一個隱藏的imageview卢厂,當(dāng)你長按哪個可拖動的view的時候礁阁,獲取到這個view的位置越走,然后通過復(fù)制這個view生成一個bitmap對象復(fù)制給framelayout中隱藏的imageview骡澈。然后根據(jù)手指位置來設(shè)置隱藏的imageview(此時顯示這個imageview)位置。
由此可見官地,所謂的拖拽其實是在拖拽一個影像亏较,影像是最先設(shè)置在framelayout中的一個imageview癣朗。通過不斷更新imageview的setX和setY來進行拖動正卧。

通過一個view來獲取其cache背景圖叉讥,從而生成一個跟其一模一樣的bitmap對象救崔。

private Bitmap createDraggedChildBitmap(View view) {
    view.setDrawingCacheEnabled(true);
    final Bitmap cache = view.getDrawingCache();

    Bitmap bitmap = null;
    if (cache != null) {
        try {
            bitmap = cache.copy(Bitmap.Config.ARGB_8888, false);
        } catch (final OutOfMemoryError e) {
            Log.w("zp_test", "Failed to copy bitmap from Drawing cache", e);
            bitmap = null;
        }
    }

    view.destroyDrawingCache();
    view.setDrawingCacheEnabled(false);

    return bitmap;
}

至于計算當(dāng)前view的位置,那么就需要你先了解以下這些內(nèi)容:

第一點
getTop(),getLeft(),getRight(),getBottom()
這四個方法是指相對于父view的位置,并且一般情況下它的值是不會發(fā)生變化的,除非有屬性動畫改變其位置,或者重新進行l(wèi)ayout方法的調(diào)用。

第二點
在拖拽監(jiān)聽中int eventX = (int) event.getX();int eventY = (int) event.getY();
不同拖拽事件對應(yīng)的eventx和eventy是不同的丹诀,具體如下:STARTED事件下對應(yīng)是拖拽點與屏幕邊界的距離(包括狀態(tài)欄)枚荣,ENTERED對應(yīng)是拖拽點與界面邊界的距離(不包括狀態(tài)欄)矢劲、LOCATION笋籽、DROP事件下對應(yīng)的是拖拽點和父view邊界的距離侍芝,END時為0。

第三點
當(dāng)進行拖拽時:
依次執(zhí)行started虱肄,entered萝挤,若干個location欺税,然后drop情组,end這樣一個執(zhí)行順序。

如下圖:

第二點示意圖1.jpg

接下來我們開工:
我們長按一個view,然后在同樣的位置生成一個影像奶赔。這個工作適合在started事件中去完成温艇。

case DragEvent.ACTION_DRAG_STARTED:
    Log.d("zp_test", "STARTED " + event.getY() + " x: " + event.getX());
    if (mDrayListener != null) {
        // 7.0以下eventX,eventY是相對于屏幕邊界線的
        View drag = getViewFromPositionRelativeScreen(eventX, eventY);
        if (drag == null) {
            Log.e("zp_test", "drag is null");
            break;
        }
        int[] location = new int[2];
        drag.getLocationInWindow(location);
        mPointToBorderX = eventX - location[0];
        mPointToBorderY = eventY - location[1];

        mDrayListener.onDragStart(createDraggedChildBitmap(drag), drag);
    }
    break;
private int[] location = new int[2];
private View getViewFromPositionRelativeScreen(int x, int y) {
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
        getLocationInWindow(location);
        return getViewFromPositionRelativeFather(x - location[0], y - location[1]);
    } else {
        return getViewFromPositionRelativeFather(x, y);
    }
}
private View getViewFromPositionRelativeFather(int x, int y) {
    Log.d("zp_test", "x: " + x + " y: " + y);
    int childCount = getChildCount();
    Log.d("zp_test", "childCount: " + childCount);
    if (childCount <= 0)
        return null;

    for (int i = 0; i < childCount; i++) {
        View view = getChildAt(i);
        Log.d("zp_test", "view l : " + view.getX()
                + " view r: " + view.getX() + view.getWidth()
                + " view t: " + view.getY()
                + " view b: " + view.getY() + view.getHeight());
        // 根據(jù)點擊位置獲取view
        if (y >= view.getY() && y <= view.getY() + view.getHeight()
                && x >= view.getX() && x <= view.getX() + view.getWidth())
            return view;
    }

    return null;
}

上面兩個方法就可以從點擊點獲取到點擊在哪個view上误阻。
注意以下兩句代碼很重要
mPointToBorderX = eventX - location[0]; mPointToBorderY = eventY - location[1];
這個是在求得點擊點和被拖拽的view邊界的距離卦停,因為我們要繪制影像的位置凿跳,就必須知道被拖拽的view到其父view的位置,這個就是影像的位置博助,但是我們從監(jiān)聽方法中只能知道點擊點的坐標(biāo)位置萝喘,我們還得求得這個view邊界到父view的位置醉旦,也就是得減去點擊點到拖拽view邊界的距離
影像y坐標(biāo) = (點擊點y坐標(biāo)) - (點擊點到view邊界的距離)

示意圖2.jpg

最開始我沒有做這個計算队秩,所以導(dǎo)致每次從STARTED事件到ENTERED事件時,都會跳動一下泽艘,而這個跳動的距離實際上就是mPointToBorderX 和mPointToBorderY 。

拖拽示意圖.gif

到這里,我們就實現(xiàn)了拖拽功能了沃于,而后的功能在下篇進行介紹繁莹,最后薄风,因為只有幾個類就不分享到github了撇他,給出百度鏈接:
拖拽功能code.zip

如有錯誤芭毙,歡迎指出。(本人最近已離職锭魔,在上海如有工作推薦例证,麻煩各位留言或者私信我,謝謝大家C耘酢)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末织咧,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子漠秋,更是在濱河造成了極大的恐慌笙蒙,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,865評論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件庆锦,死亡現(xiàn)場離奇詭異捅位,居然都是意外死亡,警方通過查閱死者的電腦和手機搂抒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,296評論 3 399
  • 文/潘曉璐 我一進店門艇搀,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人求晶,你說我怎么就攤上這事焰雕。” “怎么了誉帅?”我有些...
    開封第一講書人閱讀 169,631評論 0 364
  • 文/不壞的土叔 我叫張陵淀散,是天一觀的道長右莱。 經(jīng)常有香客問我,道長档插,這世上最難降的妖魔是什么慢蜓? 我笑而不...
    開封第一講書人閱讀 60,199評論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮郭膛,結(jié)果婚禮上晨抡,老公的妹妹穿的比我還像新娘。我一直安慰自己则剃,他們只是感情好耘柱,可當(dāng)我...
    茶點故事閱讀 69,196評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著棍现,像睡著了一般调煎。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上己肮,一...
    開封第一講書人閱讀 52,793評論 1 314
  • 那天士袄,我揣著相機與錄音,去河邊找鬼谎僻。 笑死娄柳,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的艘绍。 我是一名探鬼主播赤拒,決...
    沈念sama閱讀 41,221評論 3 423
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼诱鞠!你這毒婦竟也來了挎挖?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,174評論 0 277
  • 序言:老撾萬榮一對情侶失蹤般甲,失蹤者是張志新(化名)和其女友劉穎肋乍,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體敷存,經(jīng)...
    沈念sama閱讀 46,699評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡墓造,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,770評論 3 343
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了锚烦。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片觅闽。...
    茶點故事閱讀 40,918評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖涮俄,靈堂內(nèi)的尸體忽然破棺而出蛉拙,到底是詐尸還是另有隱情,我是刑警寧澤彻亲,帶...
    沈念sama閱讀 36,573評論 5 351
  • 正文 年R本政府宣布孕锄,位于F島的核電站吮廉,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏畸肆。R本人自食惡果不足惜宦芦,卻給世界環(huán)境...
    茶點故事閱讀 42,255評論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望轴脐。 院中可真熱鬧调卑,春花似錦、人聲如沸大咱。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,749評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽碴巾。三九已至溯捆,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間餐抢,已是汗流浹背现使。 一陣腳步聲響...
    開封第一講書人閱讀 33,862評論 1 274
  • 我被黑心中介騙來泰國打工低匙, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留旷痕,地道東北人。 一個月前我還...
    沈念sama閱讀 49,364評論 3 379
  • 正文 我出身青樓顽冶,卻偏偏與公主長得像欺抗,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子强重,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,926評論 2 361

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,336評論 25 707
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫绞呈、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,125評論 4 61
  • 激蕩三十年讀后感 第一次聽說激蕩三十年這本書是在大三上學(xué)期管理學(xué)老師那聽說的间景,老師布置了一個任務(wù)佃声,把這兩冊書讀完,...
    解小太陽閱讀 1,615評論 0 5
  • 神也無法壓制我的勃勃雄心壯志 對啊對啊又開始寫plan了乛乛 雖然每次放假都會寫然后開學(xué)時總是發(fā)現(xiàn)[em]e400...
    埋下胡楊閱讀 313評論 0 0
  • 每個人最后的依靠都只有自己. 別盲信任何人. 你和他倘要,不會走到最后.因為圾亏,你看不透他,他封拧,跟你是同一類人志鹃,同一類人...
    Sabrina667閱讀 161評論 0 0