android UI進階之彈窗的使用(2)–實現通訊錄的彈窗效果 – Android移動開發技術文章_手機開發 Android移動開發教學課程

相信大傢都體驗過android通訊錄中的彈窗效果。如圖所示:

 

android中提供瞭QuickContactBadge來實現這一效果。這裡簡單演示下。

首先創建佈局文件:

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"               android:orientation="vertical"               android:layout_width="fill_parent"               android:layout_height="fill_parent"         >     <QuickContactBadge             android:id="@+id/badge"             android:layout_width="wrap_content"             android:layout_height="wrap_content"             android:src="@drawable/icon">     </QuickContactBadge> </LinearLayout>
很簡單,在佈局中添加一個QuickContactBadge組件即可。

在Activity中配置:

public class QuickcontactActivity extends Activity {    /** Called when the activity is first created. */    @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.main);        QuickContactBadge smallBadge = (QuickContactBadge) findViewById(R.id.badge);        // 從email關聯一個contact        smallBadge.assignContactFromEmail("notice520@gmail.com", true);        // 設置窗口模式        smallBadge.setMode(ContactsContract.QuickContact.MODE_SMALL);    }}
註意加入讀通訊錄的權限

    <uses-permission android:name="android.permission.READ_CONTACTS"></uses-permission>  

實現效果如圖:

 

但是這個組件局限性很大,彈出窗口中隻能是一些contact操作。但是仔細一想,這樣的操作並不難,不就是一個帶動畫的彈窗麼。下面就來我們自己實現一個。

實現一個帶動畫的彈窗並不難,在我的之前一篇博客中有講過彈窗PopupWindow的使用,不清楚彈窗的朋友可以去看下。在這裡實現的難點主要有這些:

1.判斷基準view在屏幕中的位置,從而確定彈窗彈出的位置以及動畫。這是非常重要的一點,或許基準在屏幕上方,那麼就要向下彈出。

2.動態的添加彈窗中的按鈕,並實現點擊

3.箭頭位置的控制。箭頭應該保持在基準的下方。

4.動畫的匹配。裡面有兩種動畫。一種是PopupWindow彈出動畫,我們通過設置彈窗的style來實現(style的用法可以參考我之前的博客)。另一種是彈窗中間的佈局的動畫。

  瞭解瞭難點以後,寫起來就方便瞭。

首先實現彈窗的佈局:

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="wrap_content"    android:layout_height="wrap_content">             <FrameLayout          android:layout_marginTop="10dip"        android:id="@+id/header2"        android:layout_width="fill_parent"        android:layout_height="wrap_content"        android:background="@drawable/quickcontact_top_frame"/>              <ImageView        android:id="@+id/arrow_up"        android:layout_width="wrap_content"        android:layout_height="wrap_content"            android:src="@drawable/quickcontact_arrow_up" />           <HorizontalScrollView        android:id="@+id/scroll"        android:layout_width="fill_parent"        android:layout_height="wrap_content"        android:fadingEdgeLength="0dip"        android:layout_below="@id/header2"        android:background="@drawable/quickcontact_slider_background"        android:scrollbars="none">        <LinearLayout            android:id="@+id/tracks"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:paddingTop="4dip"            android:paddingBottom="4dip"             android:orientation="horizontal">                    <ImageView                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:src="@drawable/quickcontact_slider_grip_left" />            <ImageView                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:src="@drawable/quickcontact_slider_grip_right" />                        </LinearLayout>                </HorizontalScrollView>    <FrameLayout        android:id="@+id/footer"        android:layout_width="fill_parent"        android:layout_height="wrap_content"        android:layout_below="@id/scroll"        android:background="@drawable/quickcontact_bottom_frame" />    <ImageView        android:id="@+id/arrow_down"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_marginTop="-1dip"        android:layout_below="@id/footer"        android:src="@drawable/quickcontact_arrow_down" /></RelativeLayout>
  窗體內部使用一個HorizontalScrollView可以實現一個滑動效果。我們可以動態的在這個佈局中添加按鈕,我們稱作Actionitem。

  寫一個ActionItem類,使得我們可以用一個ArrayList做容器,動態的添加這些actionitem。這些都是服務於第二個難點。

package com.notice.quickaction;import android.graphics.drawable.Drawable;import android.view.View.OnClickListener;/** * Action item, 每個item裡面都有一個ImageView和一個TextView */public class ActionItem {    private Drawable        icon;    private String          title;    private OnClickListener listener;    /**     * 構造器     */    public ActionItem() {    }    /**     * 帶Drawable參數的構造器     */    public ActionItem(Drawable icon) {        this.icon = icon;    }    /**     * 設置標題     */    public void setTitle(String title) {        this.title = title;    }    /**     * 獲得標題     *      * @return action title     */    public String getTitle() {        return this.title;    }    /**     * 設置圖標     */    public void setIcon(Drawable icon) {        this.icon = icon;    }    /**     * 獲得圖標     */    public Drawable getIcon() {        return this.icon;    }    /**     * 綁定監聽器     */    public void setOnClickListener(OnClickListener listener) {        this.listener = listener;    }    /**     * 獲得監聽器     */    public OnClickListener getListener() {        return this.listener;    }}
  接下來就是這個彈窗的實現瞭,我們繼承PopupWindow類。在這個類中我們需要實現通過位置設置動畫及彈出位置,並且給出一個方法供實現類調用,來動態添加item和設置動畫效果。

代碼如下:

package com.notice.quickaction;import java.util.ArrayList;import android.content.Context;import android.graphics.Rect;import android.graphics.drawable.BitmapDrawable;import android.graphics.drawable.Drawable;import android.view.Gravity;import android.view.LayoutInflater;import android.view.MotionEvent;import android.view.View;import android.view.View.OnClickListener;import android.view.View.OnTouchListener;import android.view.ViewGroup;import android.view.ViewGroup.LayoutParams;import android.view.WindowManager;import android.view.animation.Animation;import android.view.animation.AnimationUtils;import android.view.animation.Interpolator;import android.widget.ImageView;import android.widget.LinearLayout;import android.widget.PopupWindow;import android.widget.TextView;/** * 繼承彈窗,構造我們需要的彈窗 */public class QuickActions extends PopupWindow {    private final View            root;    private final ImageView       mArrowUp;    private final ImageView       mArrowDown;    private final Animation       mTrackAnim;    private final LayoutInflater  inflater;    private final Context         context;    protected final View          anchor;    protected final PopupWindow   window;    private Drawable              background            = null;    protected final WindowManager windowManager;    protected static final int    ANIM_GROW_FROM_LEFT   = 1;    protected static final int    ANIM_GROW_FROM_RIGHT  = 2;    protected static final int    ANIM_GROW_FROM_CENTER = 3;    protected static final int    ANIM_AUTO             = 4;    private int                   animStyle;    private boolean               animateTrack;    private ViewGroup             mTrack;    private ArrayList<ActionItem> actionList;    /**     * 構造器,在這裡初始化一些內容     *      * @param anchor 像我之前博客所說的理解成一個基準 彈窗以此為基準彈出     */    public QuickActions(View anchor) {        super(anchor);        this.anchor = anchor;        this.window = new PopupWindow(anchor.getContext());        // 在popwindow外點擊即關閉該window        window.setTouchInterceptor(new OnTouchListener() {            @Override            public boolean onTouch(View v, MotionEvent event) {                if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {                    QuickActions.this.window.dismiss();                    return true;                }                return false;            }        });        // 得到一個windowManager對象,用來得到窗口的一些屬性        windowManager = (WindowManager) anchor.getContext().getSystemService(Context.WINDOW_SERVICE);        actionList = new ArrayList<ActionItem>();        context = anchor.getContext();        inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);        // 裝載佈局,root即為彈出窗口的佈局        root = (ViewGroup) inflater.inflate(R.layout.quickaction, null);        // 得到上下兩個箭頭        mArrowDown = (ImageView) root.findViewById(R.id.arrow_down);        mArrowUp = (ImageView) root.findViewById(R.id.arrow_up);        setContentView(root);        mTrackAnim = AnimationUtils.loadAnimation(anchor.getContext(), R.anim.rail);        // 設置動畫的加速效果        mTrackAnim.setInterpolator(new Interpolator() {            public float getInterpolation(float t) {                final float inner = (t * 1.55f) – 1.1f;                return 1.2f – inner * inner;            }        });        // 這個是彈出窗口內的水平佈局        mTrack = (ViewGroup) root.findViewById(R.id.tracks);        animStyle = ANIM_AUTO;// 設置動畫風格        animateTrack = true;    }    /**     * 設置一個flag來標識動畫顯示     */    public void animateTrack(boolean animateTrack) {        this.animateTrack = animateTrack;    }    /**     * 設置動畫風格     */    public void setAnimStyle(int animStyle) {        this.animStyle = animStyle;    }    /**     * 增加一個action     */    public void addActionItem(ActionItem action) {        actionList.add(action);    }    /**     * 彈出彈窗     */    public void show() {        // 預處理,設置window        preShow();        int[] location = new int[2];        // 得到anchor的位置        anchor.getLocationOnScreen(location);        // 以anchor的位置構造一個矩形        Rect anchorRect = new Rect(location[0], location[1], location[0] + anchor.getWidth(), location[1]                                                                                              + anchor.getHeight());        root.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));        root.measure(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);        int rootWidth = root.getMeasuredWidth();        int rootHeight = root.getMeasuredHeight();        // 得到屏幕的寬        int screenWidth = windowManager.getDefaultDisplay().getWidth();        // 設置彈窗彈出的位置的x/y        int xPos = (screenWidth – rootWidth) / 2;        int yPos = anchorRect.top – rootHeight;        boolean onTop = true;        // 在底部彈出        if (rootHeight > anchorRect.top) {            yPos = anchorRect.bottom;            onTop = false;        }        // 根據彈出位置,設置不同方向箭頭圖片        showArrow(((onTop) ? R.id.arrow_down : R.id.arrow_up), anchorRect.centerX());        // 設置彈出動畫風格        setAnimationStyle(screenWidth, anchorRect.centerX(), onTop);        // 創建action list        createActionList();        // 在指定位置彈出彈窗        window.showAtLocation(this.anchor, Gravity.NO_GRAVITY, xPos, yPos);        // 設置彈窗內部的水平佈局的動畫        if (animateTrack) mTrack.startAnimation(mTrackAnim);    }    /**     * 預處理窗口     */    protected void preShow() {        if (root == null) {            throw new IllegalStateException("需要為彈窗設置佈局");        }        // 背景是唯一能確定popupwindow寬高的元素,這裡使用root的背景,但是需要給popupwindow設置一個空的BitmapDrawable        if (background == null) {            window.setBackgroundDrawable(new BitmapDrawable());        } else {            window.setBackgroundDrawable(background);        }        window.setWidth(WindowManager.LayoutParams.WRAP_CONTENT);        window.setHeight(WindowManager.LayoutParams.WRAP_CONTENT);        window.setTouchable(true);        window.setFocusable(true);        window.setOutsideTouchable(true);        // 指定佈局        window.setContentView(root);    }    /**     * 設置動畫風格     *      * @param screenWidth 屏幕寬底     * @param requestedX 距離屏幕左邊的距離     * @param onTop 一個flag用來標識窗口的顯示位置,如果為true則顯示在anchor的頂部     */    private void setAnimationStyle(int screenWidth, int requestedX, boolean onTop) {        // 取得屏幕左邊到箭頭中心的位置        int arrowPos = requestedX – mArrowUp.getMeasuredWidth() / 2;        // 根據animStyle設置相應動畫風格        switch (animStyle) {            case ANIM_GROW_FROM_LEFT:                window.setAnimationStyle((onTop) ? R.style.Animations_PopUpMenu_Left : R.style.Animations_PopDownMenu_Left);                break;            case ANIM_GROW_FROM_RIGHT:                window.setAnimationStyle((onTop) ? R.style.Animations_PopUpMenu_Right : R.style.Animations_PopDownMenu_Right);                break;            case ANIM_GROW_FROM_CENTER:                window.setAnimationStyle((onTop) ? R.style.Animations_PopUpMenu_Center : R.style.Animations_PopDownMenu_Center);                break;            case ANIM_AUTO:                if (arrowPos <= screenWidth / 4) {                    window.setAnimationStyle((onTop) ? R.style.Animations_PopUpMenu_Left : R.style.Animations_PopDownMenu_Left);                } else if (arrowPos > screenWidth / 4 && arrowPos < 3 * (screenWidth / 4)) {                    window.setAnimationStyle((onTop) ? R.style.Animations_PopUpMenu_Center : R.style.Animations_PopDownMenu_Center);                } else {                    window.setAnimationStyle((onTop) ? R.style.Animations_PopDownMenu_Right : R.style.Animations_PopDownMenu_Right);                }                break;        }    }    /**     * 創建action list     */    private void createActionList() {        View view;        String title;        Drawable icon;        OnClickListener listener;        int index = 1;        for (int i = 0; i < actionList.size(); i++) {            title = actionList.get(i).getTitle();            icon = actionList.get(i).getIcon();            listener = actionList.get(i).getListener();            // 得到action item            view = getActionItem(title, icon, listener);            view.setFocusable(true);            view.setClickable(true);            // 將其加入佈局            mTrack.addView(view, index);            index++;        }    }    /**     * 獲得 action item     *      * @param title action的標題     * @param icon action的圖標     * @param listener action的點擊事件監聽器     * @return action的item     */    private View getActionItem(String title, Drawable icon, OnClickListener listener) {        // 裝載action佈局        LinearLayout container = (LinearLayout) inflater.inflate(R.layout.action_item, null);        ImageView img = (ImageView) container.findViewById(R.id.icon);        TextView text = (TextView) container.findViewById(R.id.title);        if (icon != null) {            img.setImageDrawable(icon);        } else {            img.setVisibility(View.GONE);        }        if (title != null) {            text.setText(title);        } else {            text.setVisibility(View.GONE);        }        if (listener != null) {            container.setOnClickListener(listener);        }        return container;    }    /**     * 顯示箭頭     *      * @param 箭頭資源id     * @param 距離屏幕左邊的距離     */    private void showArrow(int whichArrow, int requestedX) {        final View showArrow = (whichArrow == R.id.arrow_up) ? mArrowUp : mArrowDown;        final View hideArrow = (whichArrow == R.id.arrow_up) ? mArrowDown : mArrowUp;        final int arrowWidth = mArrowUp.getMeasuredWidth();        showArrow.setVisibility(View.VISIBLE);        ViewGroup.MarginLayoutParams param = (ViewGroup.MarginLayoutParams) showArrow.getLayoutParams();        // 以此設置距離左邊的距離        param.leftMargin = requestedX – arrowWidth / 2;        hideArrow.setVisibility(View.INVISIBLE);    }}
  有點長,不過註釋都寫的很清楚瞭。show()方法完成窗口的彈出。裡面調用其他方法設置瞭窗口彈出的位置,設置瞭相應的動畫彈出風格和箭頭朝向以及位置,創建瞭action item。大傢可以從這個方法裡開始看,看每個的實現。

  最後寫個測試類。放一個Button在屏幕頂部,一個在屏幕底部。點擊彈出彈窗。

package com.notice.quickaction;import android.app.Activity;import android.os.Bundle;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;import android.widget.Toast;/** * 實現activity */public class MyQuick extends Activity {    @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.main);        // 得到一個actionItem對象        final ActionItem chart = new ActionItem();        // 設置標題,圖標,點擊事件        chart.setTitle("Chart");        chart.setIcon(getResources().getDrawable(R.drawable.chart));        chart.setOnClickListener(new OnClickListener() {            @Override            public void onClick(View v) {                Toast.makeText(MyQuick.this, "Chart selected", Toast.LENGTH_SHORT).show();            }        });        final ActionItem production = new ActionItem();        production.setTitle("Products");        production.setIcon(getResources().getDrawable(R.drawable.production));        production.setOnClickListener(new OnClickListener() {            @Override            public void onClick(View v) {                Toast.makeText(MyQuick.this, "Products selected", Toast.LENGTH_SHORT).show();            }        });        Button btn1 = (Button) this.findViewById(R.id.btn1);        // 點擊按鈕彈出        btn1.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                // 初始化一個QuickActions                QuickActions qa = new QuickActions(v);                // 為他添加actionitem                qa.addActionItem(chart);                qa.addActionItem(production);                qa.addActionItem(production);                qa.addActionItem(production);                // 設置動畫風格                qa.setAnimStyle(QuickActions.ANIM_AUTO);                qa.show();            }        });        final ActionItem dashboard = new ActionItem();        dashboard.setIcon(getResources().getDrawable(R.drawable.dashboard));        dashboard.setOnClickListener(new OnClickListener() {            @Override            public void onClick(View v) {                Toast.makeText(MyQuick.this, "dashboard selected", Toast.LENGTH_SHORT).show();            }        });        final ActionItem users = new ActionItem();        users.setIcon(getResources().getDrawable(R.drawable.users));        users.setOnClickListener(new OnClickListener() {            @Override            public void onClick(View v) {                Toast.makeText(MyQuick.this, "Products selected", Toast.LENGTH_SHORT).show();            }        });        Button btn2 = (Button) this.findViewById(R.id.btn2);        btn2.setOnClickListener(new OnClickListener() {            @Override            public void onClick(View v) {                QuickActions qa = new QuickActions(v);                qa.addActionItem(dashboard);                qa.addActionItem(users);                qa.setAnimStyle(QuickActions.ANIM_GROW_FROM_CENTER);                qa.show();            }        });    }}
  再講下PopupWindow的風格的實現。其中一個風格代碼如下:

    <style name="Animations.PopDownMenu.Left">        <item name="@android:windowEnterAnimation">@anim/grow_from_topleft_to_bottomright</item>        <item name="@android:windowExitAnimation">@anim/shrink_from_bottomright_to_topleft</item>    </style>
  寫兩個item,分別實現彈出和消失動畫。因為篇幅有限(好像已經很長瞭。。。),就不全部貼出來瞭。動畫都是一個scale加一個alpha,對動畫不熟悉的朋友可以自己研究下,從底部彈出的動畫文件grow_from_bottom.xml:

<?xml version="1.0" encoding="utf-8"?><set xmlns:android="http://schemas.android.com/apk/res/android">    <scale        android:fromXScale="0.3" android:toXScale="1.0"        android:fromYScale="0.3" android:toYScale="1.0"        android:pivotX="50%" android:pivotY="100%"        android:duration="@android:integer/config_shortAnimTime"    />    <alpha        android:interpolator="@android:anim/decelerate_interpolator"        android:fromAlpha="0.0" android:toAlpha="1.0"        android:duration="@android:integer/config_shortAnimTime"    /></set>
  

  最後來看看實現效果:

                      

  好瞭 希望大傢喜歡   有問題可以留言交流~

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。