<li id="34b3z"></li>
    <nav id="34b3z"></nav>
  1. <wbr id="34b3z"><legend id="34b3z"></legend></wbr>
    
    

        1. <form id="34b3z"></form>
          <sub id="34b3z"><table id="34b3z"><th id="34b3z"></th></table></sub>

          android UI進階之彈窗的使用(2)--實現通訊錄的彈窗效果

          [來源] 達內    [編輯] 達內   [時間]2012-09-17

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

          android中提供了QuickContactBadge來實現這一效果。這里簡單演示下。
          首先創建布局文件:
          [html] view plaincopy
          1. <?xml version="1.0" encoding="utf-8"?>
          2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
          3. android:orientation="vertical"
          4. android:layout_width="fill_parent"
          5. android:layout_height="fill_parent"
          6. >
          7. <QuickContactBadge
          8. android:id="@+id/badge"
          9. android:layout_width="wrap_content"
          10. android:layout_height="wrap_content"
          11. android:src="@drawable/icon">
          12. </QuickContactBadge>
          13. </LinearLayout>
          很簡單,在布局中添加一個QuickContactBadge組件即可。
          在Activity中配置:
          [java] view plaincopy
          1. public class QuickcontactActivity extends Activity {
          2.
          3. /** Called when the activity is first created. */
          4. @Override
          5. public void onCreate(Bundle savedInstanceState) {
          6. super.onCreate(savedInstanceState);
          7. setContentView(R.layout.main);
          8.
          9. QuickContactBadge smallBadge = (QuickContactBadge) findViewById(R.id.badge);
          10. // 從email關聯一個contact
          11. smallBadge.assignContactFromEmail("notice520@gmail.com", true);
          12. // 設置窗口模式
          13. smallBadge.setMode(ContactsContract.QuickContact.MODE_SMALL);
          14. }
          15. }

          注意加入讀通訊錄的權限
          <uses-permission android:name="android.permission.READ_CONTACTS"></uses-permission>  

          但是這個組件局限性很大,彈出窗口中只能是一些contact操作。但是仔細一想,這樣的操作并不難,不就是一個帶動畫的彈窗么。下面就來我們自己實現一個。
          實現一個帶動畫的彈窗并不難,在我的之前一篇博客中有講過彈窗PopupWindow的使用,不清楚彈窗的朋友可以去看下。在這里實現的難點主要有這些:
          1.判斷基準view在屏幕中的位置,從而確定彈窗彈出的位置以及動畫。這是非常重要的一點,或許基準在屏幕上方,那么就要向下彈出。
          2.動態的添加彈窗中的按鈕,并實現點擊
          3.箭頭位置的控制。箭頭應該保持在基準的下方。
          4.動畫的匹配。里面有兩種動畫。一種是PopupWindow彈出動畫,我們通過設置彈窗的style來實現(style的用法可以參考我之前的博客)。另一種是彈窗中間的布局的動畫。
          了解了難點以后,寫起來就方便了。
          首先實現彈窗的布局:
          [html] view plaincopy
          1. <?xml version="1.0" encoding="utf-8"?>
          2. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
          3. android:layout_width="wrap_content"
          4. android:layout_height="wrap_content">
          5.
          6. <FrameLayout
          7. android:layout_marginTop="10dip"
          8. android:id="@+id/header2"
          9. android:layout_width="fill_parent"
          10. android:layout_height="wrap_content"
          11. android:background="@drawable/quickcontact_top_frame"/>
          12.
          13. <ImageView
          14. android:id="@+id/arrow_up"
          15. android:layout_width="wrap_content"
          16. android:layout_height="wrap_content"
          17. android:src="@drawable/quickcontact_arrow_up" />
          18.
          19. <HorizontalScrollView
          20. android:id="@+id/scroll"
          21. android:layout_width="fill_parent"
          22. android:layout_height="wrap_content"
          23. android:fadingEdgeLength="0dip"
          24. android:layout_below="@id/header2"
          25. android:background="@drawable/quickcontact_slider_background"
          26. android:scrollbars="none">
          27.
          28. <LinearLayout
          29. android:id="@+id/tracks"
          30. android:layout_width="wrap_content"
          31. android:layout_height="wrap_content"
          32. android:paddingTop="4dip"
          33. android:paddingBottom="4dip"
          34. android:orientation="horizontal">
          35.
          36. <ImageView
          37. android:layout_width="wrap_content"
          38. android:layout_height="wrap_content"
          39. android:src="@drawable/quickcontact_slider_grip_left" />
          40.
          41. <ImageView
          42. android:layout_width="wrap_content"
          43. android:layout_height="wrap_content"
          44. android:src="@drawable/quickcontact_slider_grip_right" />
          45.
          46. </LinearLayout>
          47.
          48. </HorizontalScrollView>
          49.
          50. <FrameLayout
          51. android:id="@+id/footer"
          52. android:layout_width="fill_parent"
          53. android:layout_height="wrap_content"
          54. android:layout_below="@id/scroll"
          55. android:background="@drawable/quickcontact_bottom_frame" />
          56.
          57. <ImageView
          58. android:id="@+id/arrow_down"
          59. android:layout_width="wrap_content"
          60. android:layout_height="wrap_content"
          61. android:layout_marginTop="-1dip"
          62. android:layout_below="@id/footer"
          63. android:src="@drawable/quickcontact_arrow_down" />
          64.
          65. </RelativeLayout>

          窗體內部使用一個HorizontalScrollView可以實現一個滑動效果。我們可以動態的在這個布局中添加按鈕,我們稱作Actionitem。
          寫一個ActionItem類,使得我們可以用一個ArrayList做容器,動態的添加這些actionitem。這些都是服務于第二個難點。
          [java] view plaincopy
          1. package com.notice.quickaction;
          2.
          3. import android.graphics.drawable.Drawable;
          4. import android.view.View.OnClickListener;
          5.
          6. /**
          7. * Action item, 每個item里面都有一個ImageView和一個TextView
          8. */
          9. public class ActionItem {
          10.
          11. private Drawable icon;
          12. private String title;
          13. private OnClickListener listener;
          14.
          15. /**
          16. * 構造器
          17. */
          18. public ActionItem() {
          19. }
          20.
          21. /**
          22. * 帶Drawable參數的構造器
          23. */
          24. public ActionItem(Drawable icon) {
          25. this.icon = icon;
          26. }
          27.
          28. /**
          29. * 設置標題
          30. */
          31. public void setTitle(String title) {
          32. this.title = title;
          33. }
          34.
          35. /**
          36. * 獲得標題
          37. *
          38. * @return action title
          39. */
          40. public String getTitle() {
          41. return this.title;
          42. }
          43.
          44. /**
          45. * 設置圖標
          46. */
          47. public void setIcon(Drawable icon) {
          48. this.icon = icon;
          49. }
          50.
          51. /**
          52. * 獲得圖標
          53. */
          54. public Drawable getIcon() {
          55. return this.icon;
          56. }
          57.
          58. /**
          59. * 綁定監聽器
          60. */
          61. public void setOnClickListener(OnClickListener listener) {
          62. this.listener = listener;
          63. }
          64.
          65. /**
          66. * 獲得監聽器
          67. */
          68. public OnClickListener getListener() {
          69. return this.listener;
          70. }
          71. }

          接下來就是這個彈窗的實現了,我們繼承PopupWindow類。在這個類中我們需要實現通過位置設置動畫及彈出位置,并且給出一個方法供實現類調用,來動態添加item和設置動畫效果。
          代碼如下:
          [java] view plaincopy
          1. /**
          2. * 繼承彈窗,構造我們需要的彈窗
          3. */
          4. public class QuickActions extends PopupWindow {
          5.
          6. private final View root;
          7. private final ImageView mArrowUp;
          8. private final ImageView mArrowDown;
          9. private final Animation mTrackAnim;
          10. private final LayoutInflater inflater;
          11. private final Context context;
          12.
          13. protected final View anchor;
          14. protected final PopupWindow window;
          15. private Drawable background = null;
          16. protected final WindowManager windowManager;
          17.
          18. protected static final int ANIM_GROW_FROM_LEFT = 1;
          19. protected static final int ANIM_GROW_FROM_RIGHT = 2;
          20. protected static final int ANIM_GROW_FROM_CENTER = 3;
          21. protected static final int ANIM_AUTO = 4;
          22.
          23. private int animStyle;
          24. private boolean animateTrack;
          25. private ViewGroup mTrack;
          26. private ArrayList<ActionItem> actionList;
          27.
          28. /**
          29. * 構造器,在這里初始化一些內容
          30. *
          31. * @param anchor 像我之前博客所說的理解成一個基準 彈窗以此為基準彈出
          32. */
          33. public QuickActions(View anchor) {
          34. super(anchor);
          35.
          36. this.anchor = anchor;
          37. this.window = new PopupWindow(anchor.getContext());
          38.
          39. // 在popwindow外點擊即關閉該window
          40. window.setTouchInterceptor(new OnTouchListener() {
          41.
          42. @Override
          43. public boolean onTouch(View v, MotionEvent event) {
          44. if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
          45. QuickActions.this.window.dismiss();
          46.
          47. return true;
          48. }
          49.
          50. return false;
          51. }
          52. });
          53.
          54. // 得到一個windowManager對象,用來得到窗口的一些屬性
          55. windowManager = (WindowManager) anchor.getContext().getSystemService(Context.WINDOW_SERVICE);
          56.
          57. actionList = new ArrayList<ActionItem>();
          58. context = anchor.getContext();
          59. inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
          60.
          61. // 裝載布局,root即為彈出窗口的布局
          62. root = (ViewGroup) inflater.inflate(R.layout.quickaction, null);
          63.
          64. // 得到上下兩個箭頭
          65. mArrowDown = (ImageView) root.findViewById(R.id.arrow_down);
          66. mArrowUp = (ImageView) root.findViewById(R.id.arrow_up);
          67.
          68. setContentView(root);
          69.
          70. mTrackAnim = AnimationUtils.loadAnimation(anchor.getContext(), R.anim.rail);
          71.
          72. // 設置動畫的加速效果
          73. mTrackAnim.setInterpolator(new Interpolator() {
          74.
          75. public float getInterpolation(float t) {
          76.
          77. final float inner = (t * 1.55f) - 1.1f;
          78.
          79. return 1.2f - inner * inner;
          80. }
          81. });
          82.
          83. // 這個是彈出窗口內的水平布局
          84. mTrack = (ViewGroup) root.findViewById(R.id.tracks);
          85. animStyle = ANIM_AUTO;// 設置動畫風格
          86. animateTrack = true;
          87. }
          88.
          89. /**
          90. * 設置一個flag來標識動畫顯示
          91. */
          92. public void animateTrack(boolean animateTrack) {
          93. this.animateTrack = animateTrack;
          94. }
          95.
          96. /**
          97. * 設置動畫風格
          98. */
          99. public void setAnimStyle(int animStyle) {
          100. this.animStyle = animStyle;
          101. }
          102.
          103. /**
          104. * 增加一個action
          105. */
          106. public void addActionItem(ActionItem action) {
          107. actionList.add(action);
          108. }
          109.
          110. /**
          111. * 彈出彈窗
          112. */
          113. public void show() {
          114. // 預處理,設置window
          115. preShow();
          116.
          117. int[] location = new int[2];
          118. // 得到anchor的位置
          119. anchor.getLocationOnScreen(location);
          120.
          121. // 以anchor的位置構造一個矩形
          122. Rect anchorRect = new Rect(location[0], location[1], location[0] + anchor.getWidth(), location[1]
          123. + anchor.getHeight());
          124.
          125. root.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
          126. root.measure(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
          127.
          128. int rootWidth = root.getMeasuredWidth();
          129. int rootHeight = root.getMeasuredHeight();
          130.
          131. // 得到屏幕的寬
          132. int screenWidth = windowManager.getDefaultDisplay().getWidth();
          133.
          134. // 設置彈窗彈出的位置的x/y
          135. int xPos = (screenWidth - rootWidth) / 2;
          136. int yPos = anchorRect.top - rootHeight;
          137.
          138. boolean onTop = true;
          139.
          140. // 在底部彈出
          141. if (rootHeight > anchorRect.top) {
          142. yPos = anchorRect.bottom;
          143. onTop = false;
          144. }
          145.
          146. // 根據彈出位置,設置不同方向箭頭圖片
          147. showArrow(((onTop) ? R.id.arrow_down : R.id.arrow_up), anchorRect.centerX());
          148.
          149. // 設置彈出動畫風格
          150. setAnimationStyle(screenWidth, anchorRect.centerX(), onTop);
          151.
          152. // 創建action list
          153. createActionList();
          154.
          155. // 在指定位置彈出彈窗
          156. window.showAtLocation(this.anchor, Gravity.NO_GRAVITY, xPos, yPos);
          157.
          158. // 設置彈窗內部的水平布局的動畫
          159. if (animateTrack) mTrack.startAnimation(mTrackAnim);
          160. }
          161.
          162. /**
          163. * 預處理窗口
          164. */
          165. protected void preShow() {
          166. if (root == null) {
          167. throw new IllegalStateException("需要為彈窗設置布局");
          168. }
          169.
          170. // 背景是唯一能確定popupwindow寬高的元素,這里使用root的背景,但是需要給popupwindow設置一個空的BitmapDrawable
          171. if (background == null) {
          172. window.setBackgroundDrawable(new BitmapDrawable());
          173. } else {
          174. window.setBackgroundDrawable(background);
          175. }
          176.
          177. window.setWidth(WindowManager.LayoutParams.WRAP_CONTENT);
          178. window.setHeight(WindowManager.LayoutParams.WRAP_CONTENT);
          179. window.setTouchable(true);
          180. window.setFocusable(true);
          181. window.setOutsideTouchable(true);
          182. // 指定布局
          183. window.setContentView(root);
          184. }
          185.
          186. /**
          187. * 設置動畫風格
          188. *
          189. * @param screenWidth 屏幕寬底
          190. * @param requestedX 距離屏幕左邊的距離
          191. * @param onTop 一個flag用來標識窗口的顯示位置,如果為true則顯示在anchor的頂部
          192. */
          193. private void setAnimationStyle(int screenWidth, int requestedX, boolean onTop) {
          194. // 取得屏幕左邊到箭頭中心的位置
          195. int arrowPos = requestedX - mArrowUp.getMeasuredWidth() / 2;
          196. // 根據animStyle設置相應動畫風格
          197. switch (animStyle) {
          198. case ANIM_GROW_FROM_LEFT:
          199. window.setAnimationStyle((onTop) ? R.style.Animations_PopUpMenu_Left : R.style.Animations_PopDownMenu_Left);
          200. break;
          201.
          202. case ANIM_GROW_FROM_RIGHT:
          203. window.setAnimationStyle((onTop) ? R.style.Animations_PopUpMenu_Right : R.style.Animations_PopDownMenu_Right);
          204. break;
          205.
          206. case ANIM_GROW_FROM_CENTER:
          207. window.setAnimationStyle((onTop) ? R.style.Animations_PopUpMenu_Center : R.style.Animations_PopDownMenu_Center);
          208. break;
          209.
          210. case ANIM_AUTO:
          211. if (arrowPos <= screenWidth / 4) {
          212. window.setAnimationStyle((onTop) ? R.style.Animations_PopUpMenu_Left : R.style.Animations_PopDownMenu_Left);
          213. } else if (arrowPos > screenWidth / 4 && arrowPos < 3 * (screenWidth / 4)) {
          214. window.setAnimationStyle((onTop) ? R.style.Animations_PopUpMenu_Center : R.style.Animations_PopDownMenu_Center);
          215. } else {
          216. window.setAnimationStyle((onTop) ? R.style.Animations_PopDownMenu_Right : R.style.Animations_PopDownMenu_Right);
          217. }
          218.
          219. break;
          220. }
          221. }
          222.
          223. /**
          224. * 創建action list
          225. */
          226. private void createActionList() {
          227. View view;
          228. String title;
          229. Drawable icon;
          230. OnClickListener listener;
          231. int index = 1;
          232.
          233. for (int i = 0; i < actionList.size(); i++) {
          234. title = actionList.get(i).getTitle();
          235. icon = actionList.get(i).getIcon();
          236. listener = actionList.get(i).getListener();
          237. // 得到action item
          238. view = getActionItem(title, icon, listener);
          239.
          240. view.setFocusable(true);
          241. view.setClickable(true);
          242.
          243. // 將其加入布局
          244. mTrack.addView(view, index);
          245.
          246. index++;
          247. }
          248. }
          249.
          250. /**
          251. * 獲得 action item
          252. *
          253. * @param title action的標題
          254. * @param icon action的圖標
          255. * @param listener action的點擊事件監聽器
          256. * @return action的item
          257. */
          258. private View getActionItem(String title, Drawable icon, OnClickListener listener) {
          259. // 裝載action布局
          260. LinearLayout container = (LinearLayout) inflater.inflate(R.layout.action_item, null);
          261. ImageView img = (ImageView) container.findViewById(R.id.icon);
          262. TextView text = (TextView) container.findViewById(R.id.title);
          263.
          264. if (icon != null) {
          265. img.setImageDrawable(icon);
          266. } else {
          267. img.setVisibility(View.GONE);
          268. }
          269.
          270. if (title != null) {
          271. text.setText(title);
          272. } else {
          273. text.setVisibility(View.GONE);
          274. }
          275.
          276. if (listener != null) {
          277. container.setOnClickListener(listener);
          278. }
          279.
          280. return container;
          281. }
          282.
          283. /**
          284. * 顯示箭頭
          285. *
          286. * @param 箭頭資源id
          287. * @param 距離屏幕左邊的距離
          288. */
          289. private void showArrow(int whichArrow, int requestedX) {
          290. final View showArrow = (whichArrow == R.id.arrow_up) ? mArrowUp : mArrowDown;
          291. final View hideArrow = (whichArrow == R.id.arrow_up) ? mArrowDown : mArrowUp;
          292.
          293. final int arrowWidth = mArrowUp.getMeasuredWidth();
          294.
          295. showArrow.setVisibility(View.VISIBLE);
          296.
          297. ViewGroup.MarginLayoutParams param = (ViewGroup.MarginLayoutParams) showArrow.getLayoutParams();
          298.
          299. // 以此設置距離左邊的距離
          300. param.leftMargin = requestedX - arrowWidth / 2;
          301.
          302. hideArrow.setVisibility(View.INVISIBLE);
          303. }
          304.
          305. }

           有點長,不過注釋都寫的很清楚了。show()方法完成窗口的彈出。里面調用其他方法設置了窗口彈出的位置,設置了相應的動畫彈出風格和箭頭朝向以及位置,創建了action item。大家可以從這個方法里開始看,看每個的實現。
          最后寫個測試類。放一個Button在屏幕頂部,一個在屏幕底部。點擊彈出彈窗。
          [java] view plaincopy
          1. package com.notice.quickaction;
          2.
          3. import android.app.Activity;
          4. import android.os.Bundle;
          5. import android.view.View;
          6. import android.view.View.OnClickListener;
          7. import android.widget.Button;
          8. import android.widget.Toast;
          9.
          10. /**
          11. * 實現activity
          12. */
          13. public class MyQuick extends Activity {
          14.
          15. @Override
          16. public void onCreate(Bundle savedInstanceState) {
          17. super.onCreate(savedInstanceState);
          18.
          19. setContentView(R.layout.main);
          20.
          21. // 得到一個actionItem對象
          22. final ActionItem chart = new ActionItem();
          23.
          24. // 設置標題,圖標,點擊事件
          25. chart.setTitle("Chart");
          26. chart.setIcon(getResources().getDrawable(R.drawable.chart));
          27. chart.setOnClickListener(new OnClickListener() {
          28.
          29. @Override
          30. public void onClick(View v) {
          31. Toast.makeText(MyQuick.this, "Chart selected", Toast.LENGTH_SHORT).show();
          32. }
          33. });
          34.
          35. final ActionItem production = new ActionItem();
          36.
          37. production.setTitle("Products");
          38. production.setIcon(getResources().getDrawable(R.drawable.production));
          39. production.setOnClickListener(new OnClickListener() {
          40.
          41. @Override
          42. public void onClick(View v) {
          43. Toast.makeText(MyQuick.this, "Products selected", Toast.LENGTH_SHORT).show();
          44. }
          45. });
          46.
          47. Button btn1 = (Button) this.findViewById(R.id.btn1);
          48. // 點擊按鈕彈出
          49. btn1.setOnClickListener(new View.OnClickListener() {
          50.
          51. @Override
          52. public void onClick(View v) {
          53. // 初始化一個QuickActions
          54. QuickActions qa = new QuickActions(v);
          55. // 為他添加actionitem
          56. qa.addActionItem(chart);
          57. qa.addActionItem(production);
          58. qa.addActionItem(production);
          59. qa.addActionItem(production);
          60. // 設置動畫風格
          61. qa.setAnimStyle(QuickActions.ANIM_AUTO);
          62.
          63. qa.show();
          64. }
          65. });
          66.
          67. final ActionItem dashboard = new ActionItem();
          68.
          69. dashboard.setIcon(getResources().getDrawable(R.drawable.dashboard));
          70. dashboard.setOnClickListener(new OnClickListener() {
          71.
          72. @Override
          73. public void onClick(View v) {
          74. Toast.makeText(MyQuick.this, "dashboard selected", Toast.LENGTH_SHORT).show();
          75. }
          76. });
          77.
          78. final ActionItem users = new ActionItem();
          79.
          80. users.setIcon(getResources().getDrawable(R.drawable.users));
          81. users.setOnClickListener(new OnClickListener() {
          82.
          83. @Override
          84. public void onClick(View v) {
          85. Toast.makeText(MyQuick.this, "Products selected", Toast.LENGTH_SHORT).show();
          86. }
          87. });
          88.
          89. Button btn2 = (Button) this.findViewById(R.id.btn2);
          90. btn2.setOnClickListener(new OnClickListener() {
          91.
          92. @Override
          93. public void onClick(View v) {
          94. QuickActions qa = new QuickActions(v);
          95.
          96. qa.addActionItem(dashboard);
          97. qa.addActionItem(users);
          98. qa.setAnimStyle(QuickActions.ANIM_GROW_FROM_CENTER);
          99.
          100. qa.show();
          101. }
          102. });
          103. }
          104. }

          再講下PopupWindow的風格的實現。其中一個風格代碼如下:
          [html] view plaincopy
          1. <style name="Animations.PopDownMenu.Left">
          2. <item name="@android:windowEnterAnimation">@anim/grow_from_topleft_to_bottomright</item>
          3. <item name="@android:windowExitAnimation">@anim/shrink_from_bottomright_to_topleft</item>
          4. </style>
          寫兩個item,分別實現彈出和消失動畫。因為篇幅有限(好像已經很長了。。。),就不全部貼出來了。動畫都是一個scale加一個alpha,對動畫不熟悉的朋友可以自己研究下,從底部彈出的動畫文件grow_from_bottom.xml:
          [html] view plaincopy
          1. <?xml version="1.0" encoding="utf-8"?>
          2. <set xmlns:android="http://schemas.android.com/apk/res/android">
          3. <scale
          4. android:fromXScale="0.3" android:toXScale="1.0"
          5. android:fromYScale="0.3" android:toYScale="1.0"
          6. android:pivotX="50%" android:pivotY="100%"
          7. android:duration="@android:integer/config_shortAnimTime"
          8. />
          9. <alpha
          10. android:interpolator="@android:anim/decelerate_interpolator"
          11. android:fromAlpha="0.0" android:toAlpha="1.0"
          12. android:duration="@android:integer/config_shortAnimTime"
          13. />
          14. </set>
           

          資源下載

          ?