Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android開發系列(十五) QQ聊天界面升級版——QQ聊天表情與SpannableString

Android開發系列(十五) QQ聊天界面升級版——QQ聊天表情與SpannableString

編輯:關於Android編程

一、表情列表的實現     需要用到的控件:GridView和ViewFlipper用GridView存放表情,因為可能有多個頁面的表情,所以要把GridView放進ViewFlipper中     首先講一下GridView:GridView類似於ListView,也需要自己定義適配器,只不過ListView的每一個條目是一行一行排放的,而GridView則是以格子的形式排放的,因此完全可以按照ListView的方式設置這裡的GridView,GridView有兩個特殊的方法:        setNumColumns()  設置GridView的行數;        setSelector(new ColorDrawable(Color.TRANSPARENT)); 設置選中GridView的某一個格子時,改格子的顏色為透明色,默認是橘黃色        GridView的每一個格子我稱其為GridItem,其對應的布局為:   復制代碼 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"     android:layout_width="match_parent"     android:layout_height="match_parent"     android:background="@android:color/transparent" >          <ImageView          android:id="@+id/gridImage"         android:layout_width="30dip"         android:layout_height="30dip"         android:layout_margin="10dip"/>     </RelativeLayout> 復制代碼   GridView自定義的適配器如下(和ListView其實很像)   復制代碼 class MyGridAdapter extends BaseAdapter{           Context context;         ArrayList<HashMap<String,Object>> list;         int layout;         String[] from;         int[] to;                           public MyGridAdapter(Context context,                 ArrayList<HashMap<String, Object>> list, int layout,                 String[] from, int[] to) {             super();             this.context = context;             this.list = list;             this.layout = layout;             this.from = from;             this.to = to;         }           @Override         public int getCount() {             // TODO Auto-generated method stub             return list.size();         }           @Override         public Object getItem(int position) {             // TODO Auto-generated method stub             return null;         }           @Override         public long getItemId(int position) {             // TODO Auto-generated method stub             return position;         }           class ViewHolder{             ImageView image=null;         }         @Override         public View getView(int position, View convertView, ViewGroup parent) {             // TODO Auto-generated method stub             ViewHolder holder=null;             if(convertView==null){                 convertView=LayoutInflater.from(context).inflate(layout, null);                 holder=new ViewHolder();                 holder.image=(ImageView)convertView.findViewById(to[0]);                 convertView.setTag(holder);             }             else{                 holder=(ViewHolder)convertView.getTag();             }             holder.image.setImageResource((Integer)list.get(position).get(from[0]));             class MyGridImageClickListener implements OnClickListener{                   int position;                                  public MyGridImageClickListener(int position) {                     super();                     this.position = position;                 }                     @Override                 public void onClick(View v) {                     // TODO Auto-generated method stub                     editText.append((String)list.get(position).get("faceName"));                 }                              }             //這裡創建了一個方法內部類             holder.image.setOnClickListener(new MyGridImageClickListener(position));                                                    return convertView;         }              } 復制代碼 然後在Java代碼中通過findViewById()方法獲得GridView對象,然後設置適配器即可,關於如何配置數據後面會講。     ViewFlipper:ViewFlipper用來實現翻頁效果,即如果有多個頁數的表情,那麼把每一個GridView作為ViewFlipper的一個ChildView添加進去,形成多個頁面,最後通過設置onTouchListener實現翻頁效果,這和我之前做過的一個ImageSwitcher的思路是完全相同的,當時的那個項目完全可以用ViewFlipper實現     ViewFlipper 對象常用的一些方法:         addView()  把ChildView添加進去;         setDisplayedChild(int index)  人為設置要顯示的childView         getDisplayedChild();   獲得當前顯示的childView的索引         showNext();  顯示下一個childView;         showPrevious();   顯示前一個 childView;   注意在ViewFlipper中添加GridView後 ViewFlipper的onTouchListener就失效了,因此這裡為GridView設置的監聽器,根據滑動情況設置翻頁效果。   復制代碼 class MyTouchListener implements OnTouchListener{           ViewFlipper viewFlipper=null;                           public MyTouchListener(ViewFlipper viewFlipper) {             super();             this.viewFlipper = viewFlipper;         }           @Override         public boolean onTouch(View v, MotionEvent event) {             // TODO Auto-generated method stub             switch(event.getAction()){             case MotionEvent.ACTION_DOWN:startX=event.getX(); moveable=true; break;             case MotionEvent.ACTION_MOVE:                 if(moveable){                     if(event.getX()-startX>60){                         moveable=false;                         int childIndex=viewFlipper.getDisplayedChild();                         /**                          * 這裡的這個if檢測是防止表情列表循環滑動                          */                         if(childIndex>0){                             viewFlipper.setInAnimation(AnimationUtils.loadAnimation(ChatActivity.this, R.anim.left_in));                             viewFlipper.setOutAnimation(AnimationUtils.loadAnimation(ChatActivity.this, R.anim.right_out));                                                     viewFlipper.showPrevious();                             setPointEffect(childIndex-1);                         }                     }                     else if(event.getX()-startX<-60){                         moveable=false;                         int childIndex=viewFlipper.getDisplayedChild();                         /**                          * 這裡的這個if檢測是防止表情列表循環滑動                          */                         if(childIndex<listGrid.size()-1){                             viewFlipper.setInAnimation(AnimationUtils.loadAnimation(ChatActivity.this, R.anim.right_in));                             viewFlipper.setOutAnimation(AnimationUtils.loadAnimation(ChatActivity.this, R.anim.left_out));                             viewFlipper.showNext();                             setPointEffect(childIndex+1);                         }                     }                 }                 break;             case MotionEvent.ACTION_UP:moveable=true;break;             default:break;             }                          return false;         }              } 復制代碼   下面講一下如何配置GridView的數據源和把GridView添加進ViewFlipper,這裡我設計了兩個方法,根據 表情圖片資源數組 faceId[]  和 表情名稱數組 faceName[] 自動生成   GridView的數據源ArrayList<ArrayList<HashMap<String,Object>>>  gridList對象,並把多個(根據表情的個數決定)GridView設置onTouchListener 然後添加進 ViewFlipper中,代碼如下:   復制代碼 private void addFaceData(){         ArrayList<HashMap<String,Object>> list=null;         for(int i=0; i<faceId.length; i++){             if(i%14==0){                 list=new ArrayList<HashMap<String,Object>>();                 listGrid.add(list);             }               HashMap<String,Object> map=new HashMap<String,Object>();             map.put("image", faceId[i]);             map.put("faceName", faceName[i]);                          /**              * 這裡把表情對應的名字也添加進數據對象中,便於在點擊時獲得表情對應的名稱              */             listGrid.get(i/14).add(map);                 }         System.out.println("listGrid size is "+listGrid.size());     }               private void addGridView(){         for(int i=0; i< listGrid.size();i++){             View view=LayoutInflater.from(this).inflate(R.layout.view_item, null);             GridView gv=(GridView)view.findViewById(R.id.myGridView);             gv.setNumColumns(5);             gv.setSelector(new ColorDrawable(Color.TRANSPARENT));             MyGridAdapter adapter=new MyGridAdapter(this, listGrid.get(i), R.layout.chat_grid_item, new String[]{"image"}, new int[]{R.id.gridImage});             gv.setAdapter(adapter);             gv.setOnTouchListener(new MyTouchListener(viewFlipper));             viewFlipper.addView(view);         //    ImageView image=new ImageView(this);         //    ImageView image=(ImageView)LayoutInflater.from(this).inflate(R.layout.image_point_layout, null);             /**              * 這裡不喜歡用Java代碼設置Image的邊框大小等,所以單獨配置了一個Imageview的布局文件              */             View pointView=LayoutInflater.from(this).inflate(R.layout.point_image_layout, null);             ImageView image=(ImageView)pointView.findViewById(R.id.pointImageView);             image.setBackgroundResource(R.drawable.qian_point);             pagePoint.addView(pointView);                /**              * 這裡驗證了LinearLayout屬於ViewGroup類型,可以采用addView 動態添加view              */                          pointList.add(image);             /**              * 將image放入pointList,便於修改點的顏色              */         }          } 復制代碼   注意addGridView最後一部分是用來設置效果圖最下方的那兩個圓點,關於實現原理接下來會講。     表情列表的布局分析:   復制代碼 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"     android:layout_width="match_parent"     android:layout_height="match_parent"     >          <LinearLayout          android:layout_width="match_parent"         android:layout_height="44dip"            android:id="@+id/chat_title"         android:layout_alignParentTop="true"         android:background="@drawable/chat_title_layer">         <Button              android:id="@+id/chat_msg_button"             android:layout_width="match_parent"             android:layout_height="36dip"             android:layout_weight="1.9"             android:layout_marginLeft="8dip"             android:layout_marginTop="3dip"             android:text="消息(0)"             android:textColor="@android:color/white"             android:textSize="7pt"             android:background="@drawable/msg_button_back"/>         <TextView              android:id="@+id/chat_contact_name"             android:layout_width="match_parent"             android:layout_height="wrap_content"             android:layout_weight="1"             android:text="龍行天下"             android:textSize="8pt"             android:textColor="@android:color/white"             android:gravity="center"             android:layout_gravity="center_vertical"/>         <ImageButton              android:id="@+id/chat_contact_button"             android:layout_width="match_parent"             android:layout_height="36dip"             android:layout_weight="2"             android:layout_marginRight="8dip"             android:layout_marginTop="3dip"             android:background="@drawable/chat_contact_back"/>              </LinearLayout>          <RelativeLayout          android:id="@+id/faceLayout"         android:layout_width="match_parent"         android:layout_height="1dip"         android:layout_alignParentBottom="true">                     <ViewFlipper              android:id="@+id/faceFlipper"             android:layout_width="match_parent"             android:layout_height="130dip"             android:background="#d0d3d5"             >         </ViewFlipper>         <LinearLayout              android:id="@+id/fill_the_gap"             android:layout_width="match_parent"             android:layout_height="1dip"             android:background="#272b34"             android:orientation="horizontal">                      </LinearLayout>         <LinearLayout              android:id="@+id/pagePoint"             android:layout_width="match_parent"             android:layout_height="20dip"             android:layout_below="@id/faceFlipper"             android:background="#d0d3d5"             android:gravity="center"             android:orientation="horizontal">                      </LinearLayout>     </RelativeLayout>                                     <LinearLayout         android:id="@+id/chat_bottom_linear"         android:layout_width="match_parent"         android:layout_height="42dip"         android:background="@drawable/chat_title_layer"           android:orientation="horizontal"         android:layout_above="@id/faceLayout"         android:paddingTop="5dip"         android:paddingBottom="3dip">                  <ImageButton              android:id="@+id/chat_bottom_look"             android:layout_width="match_parent"             android:layout_height="26dip"             android:layout_weight="3.5"             android:layout_marginLeft="7dip"             android:layout_marginTop="5dip"             android:background="@drawable/chat_bottom_look"/>         <ImageButton              android:id="@+id/chat_bottom_add"             android:layout_width="match_parent"             android:layout_height="26dip"             android:layout_weight="3.5"             android:layout_marginLeft="7dip"             android:layout_marginTop="5dip"             android:background="@drawable/chat_bottom_add"/>         <EditText              android:id="@+id/chat_bottom_edittext"             android:layout_width="match_parent"             android:layout_height="32dip"             android:layout_marginLeft="5dip"             android:layout_marginRight="7dip"             android:layout_weight="1.5"             android:background="@drawable/edit_fillet_shape"/>           <Button             android:id="@+id/chat_bottom_sendbutton"             android:layout_width="match_parent"             android:layout_height="26dip"             android:layout_marginBottom="9dip"             android:layout_marginRight="4dip"             android:layout_weight="3.2"             android:layout_gravity="top"             android:background="@drawable/chat_button_fillet_shape"             android:text="發送"             android:textColor="@android:color/white" /> "                       </LinearLayout>               <ListView          android:id="@+id/chat_list"         android:layout_width="match_parent"         android:layout_height="wrap_content"         android:layout_below="@id/chat_title"         android:layout_above="@id/chat_bottom_linear"         android:fadingEdge="none"         android:background="#f0f0f0"         android:divider="#aaaaaa"         android:dividerHeight="0px">             </ListView>              </RelativeLayout> 復制代碼 這是整個ChatActivity的布局文件,找到含有ViewFlipper的那一塊:   <RelativeLayout          android:id="@+id/faceLayout"         android:layout_width="match_parent" android:layout_height="1dip"         android:layout_alignParentBottom="true">                     <ViewFlipper              android:id="@+id/faceFlipper"             android:layout_width="match_parent"             android:layout_height="130dip"             android:background="#d0d3d5"             >         </ViewFlipper>         <LinearLayout              android:id="@+id/fill_the_gap"             android:layout_width="match_parent"             android:layout_height="1dip"             android:background="#272b34"             android:orientation="horizontal">                      </LinearLayout>         <LinearLayout              android:id="@+id/pagePoint"             android:layout_width="match_parent"             android:layout_height="20dip"             android:layout_below="@id/faceFlipper"             android:background="#d0d3d5"             android:gravity="center"             android:orientation="horizontal">                      </LinearLayout>     </RelativeLayout>   id為pagePoint 的的LinearLayout就是盛放圓點ImageView的一個布局空間(ViewGroup),ViewFlipper中有幾個子View就對應幾個圓點,也通過addView()添加ImageView,這一點在addGridView中有體現,為了實現好看的效果這裡為每一個圓點設置了布局文件,具體可參考我上傳的Demo中point_image_layout   復制代碼 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"     android:layout_width="wrap_content"     android:layout_height="match_parent"     android:orientation="vertical" >          <ImageView          android:id="@+id/pointImageView"         android:layout_width="6dip"         android:layout_height="6dip"         android:layout_marginLeft="8dip"         android:layout_marginRight="8dip"/>   </LinearLayout> 復制代碼   為什麼設置id為 fill_the_gap的Linearlayout? 這個比較麻煩講,在本例中我關閉表情界面的方法是把id為faceLayout的RelativeLayout高度設為1dip。我想在關閉表情時,讓表情界面回到初始狀態(即顯示第一頁表情),這樣每次打開表情界面都是第一頁,和官方QQ一樣,但是如果viewFlipper如果在屏幕上沒有一點像素顯示就無法調用setDisplayedChild(0)方法,因此需要把faceLayout的高度設為1dip 而非0dip ,這樣又帶來另一個問題是屏幕最下方出現了一條白線,不好看,所以就在ViewFlipper的最上方重疊了一個高為1dip 的色條,用於把這個白縫“封住”,大家可以嘗試直接把高度設為0dip,會發現體驗較差。   這裡封裝了一個設置高度的方法:   復制代碼 private void setFaceLayoutExpandState(boolean isexpand){         if(isexpand==false){               viewFlipper.setDisplayedChild(0);                 ViewGroup.LayoutParams params=faceLayout.getLayoutParams();             params.height=1;             faceLayout.setLayoutParams(params);                 /**height不設為0是因為,希望可以使再次打開時viewFlipper已經初始化為第一頁 避免             *再次打開ViewFlipper時畫面在動的結果,             *為了避免因為1dip的高度產生一個白縫,所以這裡在ViewFlipper所在的RelativeLayout中ViewFlipper             *上層添加了一個1dip高的黑色色塊             *             *viewFlipper必須在屏幕中有像素才能執行setDisplayedChild()操作             */             chatBottomLook.setBackgroundResource(R.drawable.chat_bottom_look);                                   }         else{             /**              * 讓軟鍵盤消失              */             ((InputMethodManager)getSystemService(INPUT_METHOD_SERVICE)).hideSoftInputFromWindow             (ChatActivity.this.getCurrentFocus().getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);                                         ViewGroup.LayoutParams params=faceLayout.getLayoutParams();             params.height=150;             faceLayout.setLayoutParams(params);                 chatBottomLook.setBackgroundResource(R.drawable.chat_bottom_keyboard);           }     } 復制代碼   另外這裡涉及到了對鍵盤的操作,主要目的是為了實現和官方版差不多的效果:   實現關閉鍵盤的方法:   ((InputMethodManager)getSystemService(INPUT_METHOD_SERVICE)).hideSoftInputFromWindow    (ChatActivity.this.getCurrentFocus().getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);   實現切換鍵盤狀態的方法:原來打開則關閉,原來關閉則打開   ((InputMethodManager)ChatActivity.this.getSystemService(INPUT_METHOD_SERVICE)).toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS);      
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved