Android拖拽詳解

Android中實(shí)現(xiàn)拖拽其實(shí)很簡單湾盒,系統(tǒng)早已經(jīng)提供了api讓我使用萝招,主要用到了View的startDrag(startDragAndDrop API24+) 方法以及OnDragListener忍级。

startDrag

先來看下方法介紹:

/**
     * Starts a drag and drop operation. When your application calls this method, it passes a
     * {@link android.view.View.DragShadowBuilder} object to the system. The
     * system calls this object's {@link DragShadowBuilder#onProvideShadowMetrics(Point, Point)}
     * to get metrics for the drag shadow, and then calls the object's
     * {@link DragShadowBuilder#onDrawShadow(Canvas)} to draw the drag shadow itself.
     * <p>
     *  Once the system has the drag shadow, it begins the drag and drop operation by sending
     *  drag events to all the View objects in your application that are currently visible. It does
     *  this either by calling the View object's drag listener (an implementation of
     *  {@link android.view.View.OnDragListener#onDrag(View,DragEvent) onDrag()} or by calling the
     *  View object's {@link android.view.View#onDragEvent(DragEvent) onDragEvent()} method.
     *  Both are passed a {@link android.view.DragEvent} object that has a
     *  {@link android.view.DragEvent#getAction()} value of
     *  {@link android.view.DragEvent#ACTION_DRAG_STARTED}.
     * </p>
     * <p>
     * Your application can invoke {@link #startDragAndDrop(ClipData, DragShadowBuilder, Object,
     * int) startDragAndDrop()} on any attached View object. The View object does not need to be
     * the one used in {@link android.view.View.DragShadowBuilder}, nor does it need to be related
     * to the View the user selected for dragging.
     * </p>
     * @param data A {@link android.content.ClipData} object pointing to the data to be
     * transferred by the drag and drop operation.
     * @param shadowBuilder A {@link android.view.View.DragShadowBuilder} object for building the
     * drag shadow.
     * @param myLocalState An {@link java.lang.Object} containing local data about the drag and
     * drop operation. When dispatching drag events to views in the same activity this object
     * will be available through {@link android.view.DragEvent#getLocalState()}. Views in other
     * activities will not have access to this data ({@link android.view.DragEvent#getLocalState()}
     * will return null).
     * <p>
     * myLocalState is a lightweight mechanism for the sending information from the dragged View
     * to the target Views. For example, it can contain flags that differentiate between a
     * a copy operation and a move operation.
     * </p>
     * @param flags Flags that control the drag and drop operation. This can be set to 0 for no
     * flags, or any combination of the following:
     *     <ul>
     *         <li>{@link #DRAG_FLAG_GLOBAL}</li>
     *         <li>{@link #DRAG_FLAG_GLOBAL_PERSISTABLE_URI_PERMISSION}</li>
     *         <li>{@link #DRAG_FLAG_GLOBAL_PREFIX_URI_PERMISSION}</li>
     *         <li>{@link #DRAG_FLAG_GLOBAL_URI_READ}</li>
     *         <li>{@link #DRAG_FLAG_GLOBAL_URI_WRITE}</li>
     *         <li>{@link #DRAG_FLAG_OPAQUE}</li>
     *     </ul>
     * @return {@code true} if the method completes successfully, or
     * {@code false} if it fails anywhere. Returning {@code false} means the system was unable to
     * do a drag, and so no drag operation is in progress.
     */
    public final boolean startDragAndDrop(ClipData data, DragShadowBuilder shadowBuilder,Object myLocalState, int flags) 

看到英文就頭大挑社?沒事,我來翻譯解釋一下炕横。

啟動拖放操作依溯。當(dāng)應(yīng)用程序調(diào)用此方法時老厌,它將傳遞一個DragShadowBuilder對象到系統(tǒng)。系統(tǒng)調(diào)用此對象的onProvideShadowMetrics(Point, Point)方法獲取拖動陰影的參數(shù)指標(biāo)黎炉,然后調(diào)用onDrawShadow(Canvas)來繪制陰影梅桩。一旦系統(tǒng)有了拖動陰影,它就開始拖拽操作拜隧,通過將拖拽事件發(fā)送到當(dāng)前可見的應(yīng)用程序中的所有視圖對象宿百。這些視圖可以通過設(shè)置OnDragListener在或者實(shí)現(xiàn)onDragEvent方法接受DragEvent(事件)來響應(yīng)和拖拽事件。

可以看到有四個參數(shù):

ClipData data

其實(shí)就是一個封裝數(shù)據(jù)的對象洪添,通過拖放操作傳遞給接受者垦页。該對象可以存放一個Item的集合,Item可以存放如下數(shù)據(jù):

public static class Item {
        final CharSequence mText;
        final String mHtmlText;
        final Intent mIntent;
        Uri mUri;
}

注意到可以存放Intent干奢,因此痊焊,通常可以將參數(shù)存入intent忿峻,然后通過靜態(tài)方法直接創(chuàng)建ClipData對象:

ClipData clipData = ClipData.newIntent("label", intent);

該數(shù)據(jù)可以在監(jiān)聽的中的DragEvent獲取

ClipData clipData = event.getClipData();

簡單點(diǎn)說就是可以將一些數(shù)據(jù)傳遞給拖拽的接受者薄啥,該拖拽其實(shí)可以跨Activity的,如果只是同一個Activity可以使用第三個參數(shù)傳遞數(shù)據(jù)逛尚。

DragShadowBuilder shadowBuilder

用于創(chuàng)建拖拽view是的陰影垄惧,也就是跟隨手指移動的視圖,通常直接使用默認(rèn)即可生成與一個原始view相同绰寞,帶有透明度的陰影:

View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(v);

Object myLocalState

當(dāng)你的拖拽行為是在同一個Activity中進(jìn)行時可以傳遞一個任意對象到逊,在監(jiān)聽中可以通過{@link android.view.DragEvent#getLocalState()}獲得。如果是跨Activity拖拽中無法訪問此數(shù)據(jù)滤钱,getLocalState()將返回null觉壶。

int flags

控制拖放操作的標(biāo)志。因?yàn)闆]有標(biāo)志可以設(shè)置為0件缸,flag標(biāo)志拖動是否可以跨越窗口以及一些訪問權(quán)限(需要API24+)铜靶。

了解了方法參數(shù)含義,接下來就是啟用拖拽了他炊,通常會通過長按來觸發(fā)拖拽:

iv.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View v) {
                View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(v);
                v.startDrag(null, shadowBuilder, null, 0);
                //震動反饋
                v.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
                return true;
            }
        });

開始拖拽后還要有來接受這些拖拽事件争剿,這就需要OnDragListener了。

OnDragListener

OnDragListener是在View中定義的接口佑稠,用于響應(yīng)拖拽事件,可以通過View的setOnDragListener 方法設(shè)置監(jiān)聽秒梅,有點(diǎn)類似于點(diǎn)擊事件。

public interface OnDragListener { 
        boolean onDrag(View v, DragEvent event);
}

設(shè)置監(jiān)聽舌胶,實(shí)現(xiàn)onDrag(View v, DragEvent event)方法捆蜀,其中View是設(shè)置該監(jiān)聽的view,DragEvent是拖拽事件幔嫂,可以通過event.getAction() 獲取具體事件類型辆它,這和TouchEvent非常類似,具體事件類型有如下幾種:

fl_blue.setOnDragListener(new View.OnDragListener() {
            @Override
            public boolean onDrag(View v, DragEvent event) {
                //v 永遠(yuǎn)是設(shè)置該監(jiān)聽的view履恩,這里即fl_blue
                String simpleName = v.getClass().getSimpleName();
                Log.w(BLUE, "view name:" + simpleName);
                
                //獲取事件
                int action = event.getAction();
                switch (action) {
                    case DragEvent.ACTION_DRAG_STARTED:
                        Log.i(BLUE, "開始拖拽");
                        break;
                    case DragEvent.ACTION_DRAG_ENDED:
                        Log.i(BLUE, "結(jié)束拖拽");
                        break;
                    case DragEvent.ACTION_DRAG_ENTERED:
                        Log.i(BLUE, "拖拽的view進(jìn)入監(jiān)聽的view時");
                        break;
                    case DragEvent.ACTION_DRAG_EXITED:
                        Log.i(BLUE, "拖拽的view離開監(jiān)聽的view時");
                        break;
                    case DragEvent.ACTION_DRAG_LOCATION:
                        float x = event.getX();
                        float y = event.getY();
                        long l = SystemClock.currentThreadTimeMillis();
                        Log.i(BLUE, "拖拽的view在監(jiān)聽view中的位置:x =" + x + ",y=" + y);
                        break;
                    case DragEvent.ACTION_DROP:
                        Log.i(BLUE, "釋放拖拽的view");
                        break;
                }
                //是否響應(yīng)拖拽事件锰茉,true響應(yīng),返回false只能接受到ACTION_DRAG_STARTED事件切心,后續(xù)事件不會收到
                return true;
            }
        });

此處通過event.getX(); event.getY(); 獲取的x飒筑,y是手指(也即是被拖拽view的中心點(diǎn))在監(jiān)聽view的位置片吊。

釋放手指會觸發(fā)ACTION_DRAG_ENDED 事件,如果此時被拖拽的view正好在監(jiān)聽的view中协屡,則會先觸發(fā)ACTION_DROP 事件俏脊。

這里寫圖片描述

可以同時有多個view設(shè)置拖拽監(jiān)聽接受事件,我給紅色和藍(lán)色view都設(shè)置了OnDragListener肤晓,然后拖動Android圖片到藍(lán)色區(qū)域后釋放爷贫,可以看到日志如下:

03-09 14:53:54.518 12937-12937/com.huburt.app.androiddrag I/RED: 開始拖拽
03-09 14:53:54.518 12937-12937/com.huburt.app.androiddrag I/BLUE: 開始拖拽
03-09 14:53:55.689 12937-12937/com.huburt.app.androiddrag I/BLUE: 拖拽的view進(jìn)入監(jiān)聽的view時
03-09 14:53:55.689 12937-12937/com.huburt.app.androiddrag I/BLUE: 拖拽的view在BLUE中的位置:x =111.0,y=2.0
03-09 14:53:55.870 12937-12937/com.huburt.app.androiddrag I/BLUE: 拖拽的view在BLUE中的位置:x =112.0,y=23.0
03-09 14:53:56.014 12937-12937/com.huburt.app.androiddrag I/BLUE: 釋放拖拽的view
03-09 14:53:56.017 12937-12937/com.huburt.app.androiddrag I/RED: 結(jié)束拖拽
03-09 14:53:56.017 12937-12937/com.huburt.app.androiddrag I/BLUE: 結(jié)束拖拽

現(xiàn)在我們已經(jīng)可以把Android圖片拖出來,但是還不能把它放入目標(biāo)view补憾,其實(shí)也挺簡單的漫萄,只需要在ACTION_DROP事件做一些處理即可:

            case DragEvent.ACTION_DROP:
                        Log.i(BLUE, "釋放拖拽的view");
                        ImageView localState = (ImageView) event.getLocalState();
                        FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
                        layoutParams.topMargin = (int) event.getY() - localState.getWidth() / 2;
                        layoutParams.leftMargin = (int) event.getX() - localState.getHeight() / 2;
                        ((ViewGroup) localState.getParent()).removeView(localState);
                        fl_blue.addView(localState, layoutParams);
                        break;

這里因?yàn)槭窃谕粋€Activity中,我是將拖拽的view直接傳遞過來了盈匾,當(dāng)然也可以只傳遞圖片腾务,然后在接收的view中重新new一個imageview現(xiàn)實(shí)圖片。

運(yùn)行一下就可以看到view可以拖拽到目標(biāo)位置了威酒。

可能文字描述不是特別清楚窑睁,請看demo

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市葵孤,隨后出現(xiàn)的幾起案子担钮,更是在濱河造成了極大的恐慌,老刑警劉巖尤仍,帶你破解...
    沈念sama閱讀 211,817評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件箫津,死亡現(xiàn)場離奇詭異,居然都是意外死亡宰啦,警方通過查閱死者的電腦和手機(jī)苏遥,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來赡模,“玉大人田炭,你說我怎么就攤上這事±旄蹋” “怎么了教硫?”我有些...
    開封第一講書人閱讀 157,354評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長辆布。 經(jīng)常有香客問我瞬矩,道長,這世上最難降的妖魔是什么锋玲? 我笑而不...
    開封第一講書人閱讀 56,498評論 1 284
  • 正文 為了忘掉前任景用,我火速辦了婚禮,結(jié)果婚禮上惭蹂,老公的妹妹穿的比我還像新娘伞插。我一直安慰自己割粮,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,600評論 6 386
  • 文/花漫 我一把揭開白布蜂怎。 她就那樣靜靜地躺著穆刻,像睡著了一般。 火紅的嫁衣襯著肌膚如雪杠步。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,829評論 1 290
  • 那天榜轿,我揣著相機(jī)與錄音幽歼,去河邊找鬼。 笑死谬盐,一個胖子當(dāng)著我的面吹牛甸私,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播飞傀,決...
    沈念sama閱讀 38,979評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼皇型,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了砸烦?” 一聲冷哼從身側(cè)響起弃鸦,我...
    開封第一講書人閱讀 37,722評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎幢痘,沒想到半個月后唬格,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,189評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡颜说,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,519評論 2 327
  • 正文 我和宋清朗相戀三年购岗,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片门粪。...
    茶點(diǎn)故事閱讀 38,654評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡喊积,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出玄妈,到底是詐尸還是另有隱情乾吻,我是刑警寧澤,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布措近,位于F島的核電站溶弟,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏瞭郑。R本人自食惡果不足惜辜御,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,940評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望屈张。 院中可真熱鬧擒权,春花似錦袱巨、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,762評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至剖效,卻和暖如春嫉入,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背璧尸。 一陣腳步聲響...
    開封第一講書人閱讀 31,993評論 1 266
  • 我被黑心中介騙來泰國打工咒林, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人爷光。 一個月前我還...
    沈念sama閱讀 46,382評論 2 360
  • 正文 我出身青樓垫竞,卻偏偏與公主長得像,于是被迫代替她去往敵國和親蛀序。 傳聞我的和親對象是個殘疾皇子欢瞪,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,543評論 2 349

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

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,789評論 25 707
  • ¥開啟¥ 【iAPP實(shí)現(xiàn)進(jìn)入界面執(zhí)行逐一顯】 〖2017-08-25 15:22:14〗 《//首先開一個線程,因...
    小菜c閱讀 6,367評論 0 17
  • View的事件體系 View的基礎(chǔ) view位置參數(shù)View的位置主要由它的四個頂點(diǎn)來決定徐裸,分別對應(yīng)于View的四...
    MZzF2HC閱讀 505評論 0 2
  • 繁華三月倦逐,三月繁花譬正!源于畢業(yè)班的工作,在學(xué)校這圍城之中又快半個月了檬姥,守著這群孩子曾我,從早到晚,一天又一天健民。日子...
    梓清曹老師閱讀 401評論 0 1
  • 不止一次地想過這個問題抒巢。我喜歡忙碌,因?yàn)槊β档姆戳x詞便是空閑秉犹,我不喜歡空閑所帶來的胡思亂想蛉谜,以至于只有看電視、刷手...
    Courag閱讀 313評論 0 0