簡單的橫向ListView實現(version 1.0),listviewversion

來源:互聯網
上載者:User

簡單的橫向ListView實現(version 1.0),listviewversion

最近工作不是很忙,就隨便按照自己的意願胡亂整了個小demo,簡單的實現橫向的ListView;   功能隨著版本的增加而完善,包括左右滾動、itemClick等等listView的準系統;

雖然說demo超級簡單,但是真正的寫起來倒是廢了不小的功夫。廢話少說,開始吧。

ListView肯定需要一個Adapter對象,這個小demo的核心原理就是迴圈遍曆Adapter裡面的子View,並調用子view的layout方法使其按計算的位置橫向的排列在ViewGroup上面,就構成了一個橫向的一排View;很簡單吧,看到這如果覺得對你們沒什麼協助的話,瀏覽器上本頁面上右邊的那個x,看到沒,點擊一下就ok了。

layout(int left,int top,int right,int bottom)方法是View的一個方法,該方法的四個參數決定了childView在parentView中的位置,其中(left,top)決定了childView在parentView中左上方的座標,而(right,bottom)則決定了childView在parentView 右下角的位置,其圖示如下:

其中right和bottom一般需要經過如下運算:right = left + view.getMeasureWidth(),bottom = top + view.getMeasureHeight();所以簡單的實現橫向的ListView還是很容易的;如:



如,假設item的寬度和高度為width和height,那麼在不考慮padding的情況下就有如下的關係:

A.layout(0,0,width,height),

B..layout(width,0,2*width,height)

C.layout(2*width,0,3*width,heigth);

用代碼體現出來就是:

int childLeft = 0;for(int i=0;i<getChildCount();i++) {View child = getChildAt(i);int childWidth = child.getMeasuredWidth();child.layout(childLeft, 0, childWidth+childLeft, child.getMeasuredHeight());childLeft +=  childWidth+child.getPaddingRight();}

當然layout之前需要通過addView把Adapter.getView產生的View添加的viewGroup中去的,代碼如下:

for(int i=0;i<listAdapter.getCount();i++) {View child = listAdapter.getView(i, null, this);                //注意在addview之前要對view進行measure操作                child = measureChild(child);    addView(child);}

所以簡單的橫向ListView的代碼可以出爐了:

public class HListView extends ViewGroup{/**儲存資料用的Adapter**/private ListAdapter listAdapter;public HListView(Context context) {super(context);}public HListView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);}public HListView(Context context, AttributeSet attrs) {super(context, attrs);}public ListAdapter getAdapter() {return listAdapter;}public void setAdapter(ListAdapter adapter) {this.listAdapter = adapter;}/** * 測量每個child的寬和高 * @param view * @return */private View measureChild(View view) {LayoutParams params = view.getLayoutParams();if(params==null) {params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);view.setLayoutParams(params);}view.measure(MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(getHeight(),MeasureSpec.AT_MOST));return view;}@Overrideprotected void onLayout(boolean changed, int left, int top, int right,int bottom) {if(listAdapter==null) {return;}         //把listView中的子view添加到viewGroup中for(int i=0;i<listAdapter.getCount();i++) {View child = listAdapter.getView(i, null, this);child = measureChild(child);    addView(child);}//設定子view在viewGroup中的位置 int childLeft = 0;for(int i=0;i<getChildCount();i++) {View child = getChildAt(i);int childWidth = child.getMeasuredWidth();child.layout(childLeft, 0, childWidth+childLeft, child.getMeasuredHeight());childLeft +=  childWidth+child.getPaddingRight();}}}

運行一把,看看效果吧

我屮艸芔茻,報錯了;錯誤碼定位到addView上面,追蹤原始碼addView的方法如下:

public void addView(View child) {        addView(child, -1);    } public void addView(View child, int index) {        ......        addView(child, index, params);    } public void addView(View child, int index, LayoutParams params) {         ....        requestLayout();        invalidate(true);        addViewInner(child, index, params, false);    }private void addViewInner(View child, int index, LayoutParams params,            boolean preventRequestLayout) {         .......        if (child.getParent() != null) {            throw new IllegalStateException("The specified child already has a parent. " +                    "You must call removeView() on the child's parent first.");        }        ........    }

通過原始碼的追蹤可以發現,最終addView會調用addViewInner方法,而在此方法中如果chid.getParent()!=null,就會拋出異常。話說回來了,為什麼getParent()不會空呢?其實很簡單,因為在Adapter的getView(position,convertView,parentView)的第三個參數我們傳了this,在inflate方法中逐步追蹤會發現(相關可參考此博文:

//切記,此時root為null,attachToRoot在源碼中為root!=null,所以此處為falsepublic View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) {          synchronized (mConstructorArgs) {              ......            View result = root;  //根View ,為null            try {                   ....                final String name = parser.getName();  //節點名,如果是自訂View的話 就是全限定名               //該if語句暫不用看               if (TAG_MERGE.equals(name)) { // 處理<merge />標籤                     ...                } else {                      // Temp is the root view that was found in the xml                      //這個是xml檔案對應的那個根View,在item.xml檔案中就是RelativeLayout                      View temp = createViewFromTag(name, attrs);                         ViewGroup.LayoutParams params = null;                      //因為root==null,if條件不成立                    if (root != null) {                          // Create layout params that match root, if supplied                          //根據AttributeSet屬性獲得一個LayoutParams執行個體,記住調用者為root。                          params = root.generateLayoutParams(attrs);                           if (!attachToRoot) { //重新設定temp的LayoutParams                              // Set the layout params for temp if we are not                              // attaching. (If we are, we use addView, below)                              temp.setLayoutParams(params);                          }                      }                      // Inflate all children under temp                      //遍曆temp下所有的子節點,也就是xml檔案跟檔案中的所有字View                     rInflate(parser, temp, attrs);                                            //把xml檔案的根節點以及根節點中的子節點的view都添加到root中,當然此時root等於null的情況下此處是不執行的                    if (root != null && attachToRoot) {                          root.addView(temp, params);                      }                      //如果根節點為null,就直接返回xml中根節點以及根節點的子節點組成的View                    if (root == null || !attachToRoot) {                          result = temp;                      }                  }              }               ...              return result;          }      }  

如果(root!=null)會調用root.addView(temp,params)而這個root正是我們傳的this,所以在inflate解析xml檔案的時候會把convertView 通過調用addView而添加到parentview中,所以getParent()不為null.那這樣改起來就簡單了,只要把getView(position,convetView,parentView)把parentView傳一個null就可以了。

也許會有讀者說把HListView方法中addView去掉直接改成如下的代碼不也是解決的辦法嗎?改動的代碼如下:

for(int i=0;i<listAdapter.getCount();i++) {      //通過傳遞this,讓解析infate的時候調用addView            View child = listAdapter.getView(i, null, this);      child = measureChild(child);}

運行一把試試看,結果是手機螢幕上什麼都沒有,只有HlistView顯示的backgroud設定的顏色:


為毛?其實看infate方法的話,你就可以發現如果parentView傳的值不為null的話,getView方法返回的這個View就是parentView,通過列印listAdapter.getView(i,null,this) == this,可以看到返回的是true;所以我們本來要的是childView,你返回的是parentView是幾個意思,問題查到了,解決這個問題很簡單,就是在getView返回的時候,把return convertView,改成 return convertView.findViewById(R.id.item);item為xml設定檔的根節點的id

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:id="@+id/item"    android:layout_width="100dp"    android:layout_height="200dp"     >

@Overridepublic View getView(int position, View convertView, ViewGroup parent) {HolderView holderView = null;if(convertView == null ){holderView = new HolderView();convertView = LayoutInflater.from(mContext).inflate(R.layout.list_item, parent);holderView.imageView =(ImageView) convertView.findViewById(R.id.imageView);holderView.textView = (TextView) convertView.findViewById(R.id.textView);convertView.setTag(holderView);}else{holderView = (HolderView) convertView.getTag();}holderView.imageView.setImageResource((Integer) mList.get(position).get("img"));holderView.textView.setText((String) mList.get(position).get("index"));return convertView.findViewById(R.id.item);}

最終的運行效果如下:

簡單橫向listVIew version1.0版就出來了;不過現在試試最最簡易功能,看看頁面效果都還有些圖片沒有顯示完全,也無法滾動,點擊也沒有任何響應。至於功能的完善,在version2.0的時候會逐步完善。此處是原始碼下載連結

項目源碼




聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.