0

Android UI开发: 横向ListView(HorizontalListView)及一个简单相册的完整实现 (附源码下载)

本文内容:

1、横向ListView的所有实现思路;

2、其中一个最通用的思路HorizontalListView,并基于横向ListView开发一个简单的相册;

3、实现的横向ListView在点击、浏览时item背景会变色,并解决了listview里setSelected造成item的选择状态混乱的问题。

众所周知,ListView默认的方向是垂直的,但有些时候人们更喜欢横向ListView。纵观整个网络,横向ListView的实现思路如下:

1、在布局里用HorizontalScrollView包含一个ListView,参考这里;

2、利用GridView,把它的行数设为1行;

3、有人继承ListView构造了一个HorizontalScrollListView,参见:这里

4、国外一位大牛继承AdapterView<ListAdapter>构造的HorizontalListView,这是以上所有方法里本人认为最正统的方法,本文即基于此方法,参见:这里

下面看源码:

这是Activity的布局文件:activity_main.xml
  1. <span style=“font-family: ‘Comic Sans MS’; font-size: 18px;”><RelativeLayout xmlns:android=“http://schemas.android.com/apk/res/android”  
  2.     xmlns:tools=“http://schemas.android.com/tools”  
  3.     android:layout_width=“match_parent”  
  4.     android:layout_height=“match_parent”  
  5.     android:paddingBottom=“@dimen/activity_vertical_margin”  
  6.     android:paddingLeft=“@dimen/activity_horizontal_margin”  
  7.     android:paddingRight=“@dimen/activity_horizontal_margin”  
  8.     android:paddingTop=“@dimen/activity_vertical_margin”  
  9.     tools:context=“.MainActivity”   
  10.    >  
  11.   
  12.     <org.yanzi.ui.HorizontalListView  
  13.         android:id=“@+id/horizon_listview”  
  14.         android:layout_width=“match_parent”  
  15.         android:layout_height=“150dip”  
  16.        android:layout_alignParentTop=“true”  
  17.         >  
  18.     </org.yanzi.ui.HorizontalListView>  
  19.     <ImageView   
  20.         android:id=“@+id/image_preview”  
  21.         android:layout_width=“wrap_content”  
  22.         android:layout_height=“wrap_content”  
  23.         android:layout_below=“@id/horizon_listview”  
  24.         android:layout_centerInParent=“true”  
  25.         android:clickable=“true”  
  26.         android:background=“@drawable/selector_imageview_background”  
  27.         />  
  28.     <!– android:background=”@android:drawable/ic_menu_gallery” –>  
  29.   
  30. </RelativeLayout></span>  
这是横向listview的每个item的布局,图片+文字,horizontal_list_item.xml
  1. <span style=“font-family: ‘Comic Sans MS’; font-size: 18px;”><?xml version=“1.0” encoding=“utf-8”?>  
  2. <LinearLayout xmlns:android=“http://schemas.android.com/apk/res/android”  
  3.     android:layout_width=“wrap_content”  
  4.     android:layout_height=“wrap_content”  
  5.     android:paddingLeft=“2dip”  
  6.     android:paddingRight=“2dip”  
  7.     android:paddingTop=“2dip”  
  8.     android:paddingBottom=“2dip”  
  9.     android:orientation=“vertical”   
  10.     android:gravity=“center”  
  11.     android:clickable=“true”  
  12.     android:background=“@drawable/selector_item_background”>  
  13.   <ImageView   
  14.       android:id=“@+id/img_list_item”  
  15.       android:layout_width=“wrap_content”  
  16.       android:layout_height=“wrap_content”/>  
  17.   <TextView   
  18.       android:id=“@+id/text_list_item”  
  19.       android:layout_width=“match_parent”  
  20.       android:layout_height=“wrap_content”  
  21.       android:gravity=“center”/>    
  22.   
  23. </LinearLayout>  
  24. </span>  

下面文件是selector_imageview_background.xml,这是大图片你点击浏览时背景发生变化的selector,没有啥实际作用。

  1. <span style=“font-family: ‘Comic Sans MS’; font-size: 18px;”><?xml version=“1.0” encoding=“utf-8”?>  
  2. <selector xmlns:android=“http://schemas.android.com/apk/res/android”>  
  3.   
  4.     <item android:drawable=“@android:color/holo_green_light” android:state_pressed=“true”/>  
  5.     <item android:drawable=“@android:color/holo_green_light” android:state_focused=“true”/>  
  6.     <item android:drawable=“@drawable/image_background”></item>  
  7. <!– android:drawable=”@android:color/transparent” –>  
  8. </selector></span>  

下面是每个item的selector,在focus和select时颜色会发生变化:selector_item_background.xml

  1. <span style=“font-family: ‘Comic Sans MS’; font-size: 18px;”><?xml version=“1.0” encoding=“utf-8”?>  
  2. <selector xmlns:android=“http://schemas.android.com/apk/res/android”>  
  3.   
  4.     <item android:drawable=“@android:color/holo_red_light” android:state_selected=“true”/>  
  5.     <item android:drawable=“@android:color/holo_green_dark” android:state_pressed=“true”/>  
  6.     <item android:drawable=“@android:color/transparent”/>  
  7.   
  8. </selector></span>  

主程序:MainActivity.java

  1. <span style=“font-family: ‘Comic Sans MS’; font-size: 18px;”>package org.yanzi.testhorizontallistview;  
  2.   
  3. import org.yanzi.ui.HorizontalListView;  
  4. import org.yanzi.ui.HorizontalListViewAdapter;  
  5.   
  6. import android.app.Activity;  
  7. import android.os.Bundle;  
  8. import android.view.Menu;  
  9. import android.view.View;  
  10. import android.widget.AdapterView;  
  11. import android.widget.AdapterView.OnItemClickListener;  
  12. import android.widget.ImageView;  
  13.   
  14. public class MainActivity extends Activity {  
  15.     HorizontalListView hListView;  
  16.     HorizontalListViewAdapter hListViewAdapter;  
  17.     ImageView previewImg;  
  18.     View olderSelectView = null;  
  19.     @Override  
  20.     protected void onCreate(Bundle savedInstanceState) {  
  21.         super.onCreate(savedInstanceState);  
  22.         setContentView(R.layout.activity_main);  
  23.         initUI();  
  24.     }  
  25.   
  26.     @Override  
  27.     public boolean onCreateOptionsMenu(Menu menu) {  
  28.         // Inflate the menu; this adds items to the action bar if it is present.  
  29.         getMenuInflater().inflate(R.menu.main, menu);  
  30.         return true;  
  31.     }  
  32.     public void initUI(){  
  33.         hListView = (HorizontalListView)findViewById(R.id.horizon_listview);  
  34.         previewImg = (ImageView)findViewById(R.id.image_preview);  
  35.         String[] titles = {“怀师”“南怀瑾军校”“闭关”“南怀瑾”“南公庄严照”“怀师法相”};  
  36.         final int[] ids = {R.drawable.nanhuaijin_miss, R.drawable.nanhuaijin_school,  
  37.                 R.drawable.nanhuaijin_biguan, R.drawable.nanhuaijin,  
  38.                 R.drawable.nanhuaijin_zhuangyan, R.drawable.nanhuaijin_faxiang};  
  39.         hListViewAdapter = new HorizontalListViewAdapter(getApplicationContext(),titles,ids);  
  40.         hListView.setAdapter(hListViewAdapter);  
  41.         //      hListView.setOnItemSelectedListener(new OnItemSelectedListener() {  
  42.         //  
  43.         //          @Override  
  44.         //          public void onItemSelected(AdapterView<?> parent, View view,  
  45.         //                  int position, long id) {  
  46.         //              // TODO Auto-generated method stub  
  47.         //              if(olderSelected != null){  
  48.         //                  olderSelected.setSelected(false); //上一个选中的View恢复原背景  
  49.         //              }  
  50.         //              olderSelected = view;  
  51.         //              view.setSelected(true);  
  52.         //          }  
  53.         //  
  54.         //          @Override  
  55.         //          public void onNothingSelected(AdapterView<?> parent) {  
  56.         //              // TODO Auto-generated method stub  
  57.         //                
  58.         //          }  
  59.         //      });  
  60.         hListView.setOnItemClickListener(new OnItemClickListener() {  
  61.   
  62.             @Override  
  63.             public void onItemClick(AdapterView<?> parent, View view,  
  64.                     int position, long id) {  
  65.                 // TODO Auto-generated method stub  
  66. //              if(olderSelectView == null){  
  67. //                  olderSelectView = view;  
  68. //              }else{  
  69. //                  olderSelectView.setSelected(false);  
  70. //                  olderSelectView = null;  
  71. //              }  
  72. //              olderSelectView = view;  
  73. //              view.setSelected(true);  
  74.                 previewImg.setImageResource(ids[position]);  
  75.                 hListViewAdapter.setSelectIndex(position);  
  76.                 hListViewAdapter.notifyDataSetChanged();  
  77.                   
  78.             }  
  79.         });  
  80.   
  81.     }  
  82.   
  83. }  
  84. </span>  

HorizontalListView.java 这就是自定义的横向listview

  1. <span style=“font-family: ‘Comic Sans MS’; font-size: 18px;”>package org.yanzi.ui;  
  2.   
  3. /* 
  4.  * HorizontalListView.java v1.5 
  5.  * 
  6.  *  
  7.  * The MIT License 
  8.  * Copyright (c) 2011 Paul Soucy (paul@dev-smart.com) 
  9.  *  
  10.  * Permission is hereby granted, free of charge, to any person obtaining a copy 
  11.  * of this software and associated documentation files (the “Software”), to deal 
  12.  * in the Software without restriction, including without limitation the rights 
  13.  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
  14.  * copies of the Software, and to permit persons to whom the Software is 
  15.  * furnished to do so, subject to the following conditions: 
  16.  *  
  17.  * The above copyright notice and this permission notice shall be included in 
  18.  * all copies or substantial portions of the Software. 
  19.  *  
  20.  * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
  21.  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
  22.  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
  23.  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
  24.  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
  25.  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 
  26.  * THE SOFTWARE. 
  27.  * 
  28.  */  
  29.   
  30.   
  31. import java.util.LinkedList;  
  32. import java.util.Queue;  
  33.   
  34. import android.content.Context;  
  35. import android.database.DataSetObserver;  
  36. import android.graphics.Rect;  
  37. import android.util.AttributeSet;  
  38. import android.view.GestureDetector;  
  39. import android.view.GestureDetector.OnGestureListener;  
  40. import android.view.MotionEvent;  
  41. import android.view.View;  
  42. import android.widget.AdapterView;  
  43. import android.widget.ListAdapter;  
  44. import android.widget.Scroller;  
  45.   
  46. public class HorizontalListView extends AdapterView<ListAdapter> {  
  47.   
  48.     public boolean mAlwaysOverrideTouch = true;  
  49.     protected ListAdapter mAdapter;  
  50.     private int mLeftViewIndex = –1;  
  51.     private int mRightViewIndex = 0;  
  52.     protected int mCurrentX;  
  53.     protected int mNextX;  
  54.     private int mMaxX = Integer.MAX_VALUE;  
  55.     private int mDisplayOffset = 0;  
  56.     protected Scroller mScroller;  
  57.     private GestureDetector mGesture;  
  58.     private Queue<View> mRemovedViewQueue = new LinkedList<View>();  
  59.     private OnItemSelectedListener mOnItemSelected;  
  60.     private OnItemClickListener mOnItemClicked;  
  61.     private OnItemLongClickListener mOnItemLongClicked;  
  62.     private boolean mDataChanged = false;  
  63.       
  64.   
  65.     public HorizontalListView(Context context, AttributeSet attrs) {  
  66.         super(context, attrs);  
  67.         initView();  
  68.     }  
  69.       
  70.     private synchronized void initView() {  
  71.         mLeftViewIndex = –1;  
  72.         mRightViewIndex = 0;  
  73.         mDisplayOffset = 0;  
  74.         mCurrentX = 0;  
  75.         mNextX = 0;  
  76.         mMaxX = Integer.MAX_VALUE;  
  77.         mScroller = new Scroller(getContext());  
  78.         mGesture = new GestureDetector(getContext(), mOnGesture);  
  79.     }  
  80.       
  81.     @Override  
  82.     public void setOnItemSelectedListener(AdapterView.OnItemSelectedListener listener) {  
  83.         mOnItemSelected = listener;  
  84.     }  
  85.       
  86.     @Override  
  87.     public void setOnItemClickListener(AdapterView.OnItemClickListener listener){  
  88.         mOnItemClicked = listener;  
  89.     }  
  90.       
  91.     @Override  
  92.     public void setOnItemLongClickListener(AdapterView.OnItemLongClickListener listener) {  
  93.         mOnItemLongClicked = listener;  
  94.     }  
  95.   
  96.     private DataSetObserver mDataObserver = new DataSetObserver() {  
  97.   
  98.         @Override  
  99.         public void onChanged() {  
  100.             synchronized(HorizontalListView.this){  
  101.                 mDataChanged = true;  
  102.             }  
  103.             invalidate();  
  104.             requestLayout();  
  105.         }  
  106.   
  107.         @Override  
  108.         public void onInvalidated() {  
  109.             reset();  
  110.             invalidate();  
  111.             requestLayout();  
  112.         }  
  113.           
  114.     };  
  115.   
  116.     @Override  
  117.     public ListAdapter getAdapter() {  
  118.         return mAdapter;  
  119.     }  
  120.   
  121.     @Override  
  122.     public View getSelectedView() {  
  123.         //TODO: implement  
  124.         return null;  
  125.     }  
  126.   
  127.     @Override  
  128.     public void setAdapter(ListAdapter adapter) {  
  129.         if(mAdapter != null) {  
  130.             mAdapter.unregisterDataSetObserver(mDataObserver);  
  131.         }  
  132.         mAdapter = adapter;  
  133.         mAdapter.registerDataSetObserver(mDataObserver);  
  134.         reset();  
  135.     }  
  136.       
  137.     private synchronized void reset(){  
  138.         initView();  
  139.         removeAllViewsInLayout();  
  140.         requestLayout();  
  141.     }  
  142.   
  143.     @Override  
  144.     public void setSelection(int position) {  
  145.         //TODO: implement  
  146.     }  
  147.       
  148.     private void addAndMeasureChild(final View child, int viewPos) {  
  149.         LayoutParams params = child.getLayoutParams();  
  150.         if(params == null) {  
  151.             params = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);  
  152.         }  
  153.   
  154.         addViewInLayout(child, viewPos, params, true);  
  155.         child.measure(MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.AT_MOST),  
  156.                 MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.AT_MOST));  
  157.     }  
  158.       
  159.       
  160.   
  161.     @Override  
  162.     protected synchronized void onLayout(boolean changed, int left, int top, int right, int bottom) {  
  163.         super.onLayout(changed, left, top, right, bottom);  
  164.   
  165.         if(mAdapter == null){  
  166.             return;  
  167.         }  
  168.           
  169.         if(mDataChanged){  
  170.             int oldCurrentX = mCurrentX;  
  171.             initView();  
  172.             removeAllViewsInLayout();  
  173.             mNextX = oldCurrentX;  
  174.             mDataChanged = false;  
  175.         }  
  176.   
  177.         if(mScroller.computeScrollOffset()){  
  178.             int scrollx = mScroller.getCurrX();  
  179.             mNextX = scrollx;  
  180.         }  
  181.           
  182.         if(mNextX <= 0){  
  183.             mNextX = 0;  
  184.             mScroller.forceFinished(true);  
  185.         }  
  186.         if(mNextX >= mMaxX) {  
  187.             mNextX = mMaxX;  
  188.             mScroller.forceFinished(true);  
  189.         }  
  190.           
  191.         int dx = mCurrentX – mNextX;  
  192.           
  193.         removeNonVisibleItems(dx);  
  194.         fillList(dx);  
  195.         positionItems(dx);  
  196.           
  197.         mCurrentX = mNextX;  
  198.           
  199.         if(!mScroller.isFinished()){  
  200.             post(new Runnable(){  
  201.                 @Override  
  202.                 public void run() {  
  203.                     requestLayout();  
  204.                 }  
  205.             });  
  206.               
  207.         }  
  208.     }  
  209.       
  210.     private void fillList(final int dx) {  
  211.         int edge = 0;  
  212.         View child = getChildAt(getChildCount()-1);  
  213.         if(child != null) {  
  214.             edge = child.getRight();  
  215.         }  
  216.         fillListRight(edge, dx);  
  217.           
  218.         edge = 0;  
  219.         child = getChildAt(0);  
  220.         if(child != null) {  
  221.             edge = child.getLeft();  
  222.         }  
  223.         fillListLeft(edge, dx);  
  224.           
  225.           
  226.     }  
  227.       
  228.     private void fillListRight(int rightEdge, final int dx) {  
  229.         while(rightEdge + dx < getWidth() && mRightViewIndex < mAdapter.getCount()) {  
  230.               
  231.             View child = mAdapter.getView(mRightViewIndex, mRemovedViewQueue.poll(), this);  
  232.             addAndMeasureChild(child, –1);  
  233.             rightEdge += child.getMeasuredWidth();  
  234.               
  235.             if(mRightViewIndex == mAdapter.getCount()-1) {  
  236.                 mMaxX = mCurrentX + rightEdge – getWidth();  
  237.             }  
  238.               
  239.             if (mMaxX < 0) {  
  240.                 mMaxX = 0;  
  241.             }  
  242.             mRightViewIndex++;  
  243.         }  
  244.           
  245.     }  
  246.       
  247.     private void fillListLeft(int leftEdge, final int dx) {  
  248.         while(leftEdge + dx > 0 && mLeftViewIndex >= 0) {  
  249.             View child = mAdapter.getView(mLeftViewIndex, mRemovedViewQueue.poll(), this);  
  250.             addAndMeasureChild(child, 0);  
  251.             leftEdge -= child.getMeasuredWidth();  
  252.             mLeftViewIndex–;  
  253.             mDisplayOffset -= child.getMeasuredWidth();  
  254.         }  
  255.     }  
  256.       
  257.     private void removeNonVisibleItems(final int dx) {  
  258.         View child = getChildAt(0);  
  259.         while(child != null && child.getRight() + dx <= 0) {  
  260.             mDisplayOffset += child.getMeasuredWidth();  
  261.             mRemovedViewQueue.offer(child);  
  262.             removeViewInLayout(child);  
  263.             mLeftViewIndex++;  
  264.             child = getChildAt(0);  
  265.               
  266.         }  
  267.           
  268.         child = getChildAt(getChildCount()-1);  
  269.         while(child != null && child.getLeft() + dx >= getWidth()) {  
  270.             mRemovedViewQueue.offer(child);  
  271.             removeViewInLayout(child);  
  272.             mRightViewIndex–;  
  273.             child = getChildAt(getChildCount()-1);  
  274.         }  
  275.     }  
  276.       
  277.     private void positionItems(final int dx) {  
  278.         if(getChildCount() > 0){  
  279.             mDisplayOffset += dx;  
  280.             int left = mDisplayOffset;  
  281.             for(int i=0;i<getChildCount();i++){  
  282.                 View child = getChildAt(i);  
  283.                 int childWidth = child.getMeasuredWidth();  
  284.                 child.layout(left, 0, left + childWidth, child.getMeasuredHeight());  
  285.                 left += childWidth + child.getPaddingRight();  
  286.             }  
  287.         }  
  288.     }  
  289.       
  290.     public synchronized void scrollTo(int x) {  
  291.         mScroller.startScroll(mNextX, 0, x – mNextX, 0);  
  292.         requestLayout();  
  293.     }  
  294.       
  295.     @Override  
  296.     public boolean dispatchTouchEvent(MotionEvent ev) {  
  297.         boolean handled = super.dispatchTouchEvent(ev);  
  298.         handled |= mGesture.onTouchEvent(ev);  
  299.         return handled;  
  300.     }  
  301.       
  302.     protected boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,  
  303.                 float velocityY) {  
  304.         synchronized(HorizontalListView.this){  
  305.             mScroller.fling(mNextX, 0, (int)-velocityX, 00, mMaxX, 00);  
  306.         }  
  307.         requestLayout();  
  308.           
  309.         return true;  
  310.     }  
  311.       
  312.     protected boolean onDown(MotionEvent e) {  
  313.         mScroller.forceFinished(true);  
  314.         return true;  
  315.     }  
  316.       
  317.     private OnGestureListener mOnGesture = new GestureDetector.SimpleOnGestureListener() {  
  318.   
  319.         @Override  
  320.         public boolean onDown(MotionEvent e) {  
  321.             return HorizontalListView.this.onDown(e);  
  322.         }  
  323.   
  324.         @Override  
  325.         public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,  
  326.                 float velocityY) {  
  327.             return HorizontalListView.this.onFling(e1, e2, velocityX, velocityY);  
  328.         }  
  329.   
  330.         @Override  
  331.         public boolean onScroll(MotionEvent e1, MotionEvent e2,  
  332.                 float distanceX, float distanceY) {  
  333.               
  334.             synchronized(HorizontalListView.this){  
  335.                 mNextX += (int)distanceX;  
  336.             }  
  337.             requestLayout();  
  338.               
  339.             return true;  
  340.         }  
  341.   
  342.         @Override  
  343.         public boolean onSingleTapConfirmed(MotionEvent e) {  
  344.             for(int i=0;i<getChildCount();i++){  
  345.                 View child = getChildAt(i);  
  346.                 if (isEventWithinView(e, child)) {  
  347.                     if(mOnItemClicked != null){  
  348.                         mOnItemClicked.onItemClick(HorizontalListView.this, child, mLeftViewIndex + 1 + i, mAdapter.getItemId( mLeftViewIndex + 1 + i ));  
  349.                     }  
  350.                     if(mOnItemSelected != null){  
  351.                         mOnItemSelected.onItemSelected(HorizontalListView.this, child, mLeftViewIndex + 1 + i, mAdapter.getItemId( mLeftViewIndex + 1 + i ));  
  352.                     }  
  353.                     break;  
  354.                 }  
  355.                   
  356.             }  
  357.             return true;  
  358.         }  
  359.           
  360.         @Override  
  361.         public void onLongPress(MotionEvent e) {  
  362.             int childCount = getChildCount();  
  363.             for (int i = 0; i < childCount; i++) {  
  364.                 View child = getChildAt(i);  
  365.                 if (isEventWithinView(e, child)) {  
  366.                     if (mOnItemLongClicked != null) {  
  367.                         mOnItemLongClicked.onItemLongClick(HorizontalListView.this, child, mLeftViewIndex + 1 + i, mAdapter.getItemId(mLeftViewIndex + 1 + i));  
  368.                     }  
  369.                     break;  
  370.                 }  
  371.   
  372.             }  
  373.         }  
  374.   
  375.         private boolean isEventWithinView(MotionEvent e, View child) {  
  376.             Rect viewRect = new Rect();  
  377.             int[] childPosition = new int[2];  
  378.             child.getLocationOnScreen(childPosition);  
  379.             int left = childPosition[0];  
  380.             int right = left + child.getWidth();  
  381.             int top = childPosition[1];  
  382.             int bottom = top + child.getHeight();  
  383.             viewRect.set(left, top, right, bottom);  
  384.             return viewRect.contains((int) e.getRawX(), (int) e.getRawY());  
  385.         }  
  386.     };  
  387.   
  388.       
  389.   
  390. }  
  391. </span>  

HorizontalListViewAdapter.java 横向listview的适配器,我将他单独写到一个java文件里。

  1. <span style=“font-family: ‘Comic Sans MS’; font-size: 18px;”>package org.yanzi.ui;  
  2.   
  3. import org.yanzi.testhorizontallistview.R;  
  4. import org.yanzi.util.BitmapUtil;  
  5.   
  6. import android.content.Context;  
  7. import android.graphics.Bitmap;  
  8. import android.graphics.drawable.Drawable;  
  9. import android.media.ThumbnailUtils;  
  10. import android.view.LayoutInflater;  
  11. import android.view.View;  
  12. import android.view.ViewGroup;  
  13. import android.widget.BaseAdapter;  
  14. import android.widget.ImageView;  
  15. import android.widget.TextView;  
  16.   
  17. public class HorizontalListViewAdapter extends BaseAdapter{  
  18.     private int[] mIconIDs;  
  19.     private String[] mTitles;  
  20.     private Context mContext;  
  21.     private LayoutInflater mInflater;  
  22.     Bitmap iconBitmap;  
  23.     private int selectIndex = –1;  
  24.   
  25.     public HorizontalListViewAdapter(Context context, String[] titles, int[] ids){  
  26.         this.mContext = context;  
  27.         this.mIconIDs = ids;  
  28.         this.mTitles = titles;  
  29.         mInflater=(LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);//LayoutInflater.from(mContext);  
  30.     }  
  31.     @Override  
  32.     public int getCount() {  
  33.         return mIconIDs.length;  
  34.     }  
  35.     @Override  
  36.     public Object getItem(int position) {  
  37.         return position;  
  38.     }  
  39.   
  40.     @Override  
  41.     public long getItemId(int position) {  
  42.         return position;  
  43.     }  
  44.   
  45.     @Override  
  46.     public View getView(int position, View convertView, ViewGroup parent) {  
  47.   
  48.         ViewHolder holder;  
  49.         if(convertView==null){  
  50.             holder = new ViewHolder();  
  51.             convertView = mInflater.inflate(R.layout.horizontal_list_item, null);  
  52.             holder.mImage=(ImageView)convertView.findViewById(R.id.img_list_item);  
  53.             holder.mTitle=(TextView)convertView.findViewById(R.id.text_list_item);  
  54.             convertView.setTag(holder);  
  55.         }else{  
  56.             holder=(ViewHolder)convertView.getTag();  
  57.         }  
  58.         if(position == selectIndex){  
  59.             convertView.setSelected(true);  
  60.         }else{  
  61.             convertView.setSelected(false);  
  62.         }  
  63.           
  64.         holder.mTitle.setText(mTitles[position]);  
  65.         iconBitmap = getPropThumnail(mIconIDs[position]);  
  66.         holder.mImage.setImageBitmap(iconBitmap);  
  67.   
  68.         return convertView;  
  69.     }  
  70.   
  71.     private static class ViewHolder {  
  72.         private TextView mTitle ;  
  73.         private ImageView mImage;  
  74.     }  
  75.     private Bitmap getPropThumnail(int id){  
  76.         Drawable d = mContext.getResources().getDrawable(id);  
  77.         Bitmap b = BitmapUtil.drawableToBitmap(d);  
  78. //      Bitmap bb = BitmapUtil.getRoundedCornerBitmap(b, 100);  
  79.         int w = mContext.getResources().getDimensionPixelOffset(R.dimen.thumnail_default_width);  
  80.         int h = mContext.getResources().getDimensionPixelSize(R.dimen.thumnail_default_height);  
  81.           
  82.         Bitmap thumBitmap = ThumbnailUtils.extractThumbnail(b, w, h);  
  83.           
  84.         return thumBitmap;  
  85.     }  
  86.     public void setSelectIndex(int i){  
  87.         selectIndex = i;  
  88.     }  
  89. }</span>  

下面是效果图:

下图是一个item被选定后,另一个item获得了焦点:
下面是横向时的截图:

要点如下:

1、可以说这个HorizontalListView是完美的,但美中不足的并不是其他人说的不能点击、晃动、加载不全的问题,而是这个横向Listview的高度,如果你设成wrap_cotent那么将会占据整个屏幕,即使你将它适配器里的view的高度限制死,限制成很小,这个HorizontalListView的高度依然是全屏。本文代码里,我把图片缩略图弄成100dip,所以把这个HorizontalListView的高度设为了150dip。
2、在适配器里,我填充了一个图片,下面是文字。为了能让浏览图片时item有反应,搞了一个selector,它的用法详见这里. 但一开始在点击时完全没有反应,参考这里:
<?xml version=”1.0″ encoding=”utf-8″?>
<selector xmlns:android=”http://schemas.android.com/apk/res/android”>
<item android:drawable=”@android:color/holo_red_light” android:state_selected=”true”/>
<item android:drawable=”@android:color/holo_green_dark” android:state_pressed=”true”/>
<item android:drawable=”@android:color/transparent”/>
</selector>
将自然状态下的背景放到了最后,但点击浏览时依然没有作用。其实最根本原因是在布局文件里horizontal_list_item.xml要让这个布局能够clickable,即:android:clickable=”true”
3、上一步完成了,还需要点击即select一个item时,让它变色并且保持住,然后点击另外一个item时,让之前得item恢复默认背景。为了实现这个问题,我曾作如下尝试:
  1. <span style=“font-family: ‘Comic Sans MS’; font-size: 18px;”>//             if(olderSelectView == null){  
  2. //                  olderSelectView = view;  
  3. //              }else{  
  4. //                  olderSelectView.setSelected(false);  
  5. //                  olderSelectView = null;  
  6. //              }  
  7. //              olderSelectView = view;  
  8. //              view.setSelected(true);</span>  

即在click监听里,保存上一个选中的view。遗憾的是这种方法会造成item的选中状态造成混乱,比如第一个item选中了,同时第5个item也莫名其妙的被选中了。上述情况发生在滑动时,即一屏显示不完的情况下。当我横屏时,在所有的item都能一次性显示出来情况下,用上述方法么问题。后来我想到,这可以是适配器里的缓存机制造成的,最好不要再listview适配器外对item作修改,即便修改则一定要调适配器的: hListViewAdapter.notifyDataSetChanged();通知刷新view,毕竟适配器才是view的提供者。参考这位大大的文章:http://longyi-java.iteye.com/blog/976067 在适配器里加了一个接口保存选中的索引,然后再getView函数里进行判断。如果是选中的item,则将布局设为选中状态即可,horizontal_list_item.xml里的Linearlayout就会自动加载那个selector了。而无需像这个参考链接里对每个item的元素分别设置状态。

4、BitmapUtil是个工具类,负责将id转成一个bitmap,然后用android自带的ThumbnailUtils去提取缩略图。
5、之所以horizontal_list_item布局里要设置padding是为了选中item时,整个item有种被圈住的感觉,而不是光下面一点变色。
欢迎Android爱好者加群

Android您问我讲-2,

群号:19241311,备注:yanzi

 ———————————本文系原创,转载请注明作者:yanzi1225627

天边的星星