編輯:關於Android編程
我用GridView來顯示一些字符串,而字符串的長度是不固定的,然後就遇到問題了:有時字符重疊,有時顯示不全,有時兩種問題同時出現。見下圖:
圖一 GridView顯示重疊、顯示不全
這到底是什麼情況,該怎麼解決呢?
第一反應是上網查找方案,有兩種方案:
1, 自定義GridView,重寫onMeasure,如下:
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int expandSpec = MeasureSpec.makeMeasureSpec( Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST); super.onMeasure(widthMeasureSpec, expandSpec); }
經過測試,不能解決問題。
2, 先在Adapter中獲取GridView的高度,然後均分給每排:
代碼如下:
int height = mGv.getHeight(); int width = mGv.getWidth(); //得到GridView每一項的高度與寬度 if(height>0){ GridView.LayoutParams params = new GridView.LayoutParams(width / 3, height /2); //針對我的情況,3列2排,直接寫數測試 //設置每一行的高度和寬度 view.setLayoutParams(params); }
發現有時能解決問題,有時不行。當然對於圖一中遇到的問題,也是不能解決的,但是情況有所變化,截圖如下:
圖二 GridView顯示不全,不可滑動
可以看出,沒有重疊了,但是第一排的字,已經有丟失的情況發生了。第二排的也是丟失了,因為此時的GridView是不能滑動的。(圖一的情況,還可以滑動顯示全第二排的內容)
我的目標是兩排都能顯示全,而且不用滑動來進行顯示。那麼,該怎麼辦呢?既然沒有現成的解決方案,那就自己來研究下GridView的工作邏輯吧。
下面描述下我的測試環境,也就是問題現場:
主界面使用了一個GridView,裡面的子項內容是一個TextView,文本的內容是一項水果名,共有6種,分為2排3列顯示。
xml文件中,GridView的布局:
GridView子項的內容布局:
提供的數據如下:
private void initFruits() { Log.i("MainActivity", "log: initFruits"); Fruit banana = new Fruit("仙人蕉"); fruitList.add(banana); Fruit apple = new Fruit("紅嘎拉蘋果"); fruitList.add(apple); Fruit strawberry = new Fruit("玉麟草莓"); fruitList.add(strawberry); Fruit pineapple = new Fruit("北界紅提葡萄"); fruitList.add(pineapple); Fruit orange = new Fruit("褚橙"); fruitList.add(orange); Fruit watermelon = new Fruit("黑美人西瓜"); fruitList.add(watermelon); }
適配器裡面是這樣寫的:
public View getView(int position, View convertView, ViewGroup parent) { Fruit fruit = getItem(position); View view; ViewHolder viewHolder; TextView tv_record ; String strLog; if(convertView == null){ view = LayoutInflater.from(getContext()).inflate(resoureId, null); strLog = "log: new ViewHolder "; viewHolder = new ViewHolder(); viewHolder.fruitName = (TextView) view.findViewById(R.id.fruit_name); view.setTag(viewHolder); viewHolder.fruitName.setText(fruit.getName()); } else{ strLog = "log: use old ViewHolder "; view = convertView; viewHolder = (ViewHolder)view.getTag(); } tv_record = viewHolder.fruitName; Log.i("FruitAdapter", strLog+" pos="+position+" fruitName=" + fruit.getName()+" mGv.getHeight="+mGv.getHeight()); Log.i("FruitAdapter", "log: getView: lines="+tv_record.getLineCount() +" LineHeight="+ tv_record.getLineHeight()+" tv.height="+tv_record.getHeight()); return view; }
我測試的主要方法,就是看getView()中的輸出信息。如下:
07-24 22:11:58.010: I/FruitAdapter(8061): log: new ViewHolder pos=0 fruitName=仙人蕉 mGv.getHeight=0 07-24 22:11:58.010: I/FruitAdapter(8061): log: getView: lines=0 LineHeight=105 tv.height=0 07-24 22:11:58.030: I/FruitAdapter(8061): log: use old ViewHolder pos=0 fruitName=仙人蕉 mGv.getHeight=0 07-24 22:11:58.030: I/FruitAdapter(8061): log: getView: lines=2 LineHeight=105 tv.height=0 07-24 22:11:58.060: I/FruitAdapter(8061): log: use old ViewHolder pos=0 fruitName=仙人蕉 mGv.getHeight=458 07-24 22:11:58.060: I/FruitAdapter(8061): log: getView: lines=2 LineHeight=105 tv.height=0
我們可以看出,在getView()中,第一個位置的view會執行3次,
第一次,新建view,所有高度值都是0 ,除了字體的高度是105。
第二次,convertView已經存在了,我們看到lines=2,即此時已經根據文本內容計算出行數了,但是GridView的總高度仍然是0。
第三次,GridView的總高度已經有值了,但是TextView的高度仍然是0。
怎樣才能看到TextView的高度呢?
我有一次在調整文本初始化的順序時,畫面顯示不全,在滑動顯示時,看見了TextView的高度值不為0 ,此時界面如下:
圖三 GridView顯示不全,可滑動顯示
Log如下:
07-25 21:54:05.760: I/FruitAdapter(28214): log: use old ViewHolder pos=0 fruitName=褚橙 mGv.getHeight=248 07-25 21:54:05.760: I/FruitAdapter(28214): log: getView: lines=1 LineHeight=105 tv.height=124 07-25 21:54:05.760: I/FruitAdapter(28214): log: use old ViewHolder pos=1 fruitName=玉麟草莓 mGv.getHeight=248 07-25 21:54:05.760: I/FruitAdapter(28214): log: getView: lines=2 LineHeight=105 tv.height=229
有了這些數據,就能做一些分析工作了:
例如內容為“褚橙”時,字符串沒有換行,字高105,TextView高度為124,則字與控件中還有空余為124-105=19。
對於“玉麟草莓”,字分2行,字高105,兩行為105*2=210,加上空余間隔為19,加起來是210+19=229,與日志中的值相符合。
此時GridView的高度為248,恰好是124的2倍。應該是通過內容的總個數計算出來的(後面的試驗用更豐富的數據支撐了這個判斷)。
在構建 FruitAdapter 時,已經通過輸入參數 fruitList,知道總共有多少項了,本例中為6項。又已經在布局中設置了numColumns=”3” ,就知道格子有幾排了:6/3=2。這樣,按照第一個格子的高度值124,總計2排,計算出GridView的總體高度為2*124=248。
通過滑動屏幕,能看到文本總共有6行。說明下,此時文本的順序是:褚橙,玉麟草莓,北界紅提葡萄,紅嘎拉蘋果,仙人蕉,黑美人西瓜。我通過調整字體,使兩個字為一行,超過則換行。此時的6行的高度,應該是兩個3行相加的結果。第一排,毫無疑問,只有“北界紅提葡萄”是分為3行的,所以,第一行的內容高度,是取的最後一個格子的高度。第二排是不是也是使用的最後一個格子的高度呢?通過調整第二排的內容順序,以及進一步的調整到2列3排,我驗證了這個想法。
總結:
1, GridView有一個視圖高度,其計算方法是:第一個格子的高度*總的排數;
2, GridView有一個內容高度,其計算方法是:每排最後一個格子的高度之和;
3, 若視圖高度小於內容高度,則內容顯示不全,可以滑動來顯示;
4,GridView每排的高度,不是固定的,使用的是最後一個格子的高度。
5,若某一排中有內容超過最後一個格子高度的,則多出的部分,會顯示到下一排上,從而出現重疊現象。若這排就是最後一排了,則多出的部分不能顯示。
根據以上結論,我們可以分析前面3張圖中遇到的問題:
第一張圖:字符重疊的原因,第二個格子的高度(3行)大於該排中最後一個格子的高度(2行)。字符顯示不全的原因:GridView總高度=2行*2=4行,而內容高度為2行+3行=5行,內容高度大於視圖高度,從而顯示不全。
第二張圖:字符顯示不全的原因:GridView視圖高度=2行*2=4行,進行均分,則每排為2行,所有高度值超過2行的格子,超過2行的內容部分都顯示不了。
第三張圖:字符顯示不全的原因:GridView總高度=1行*2=2行,而內容高度為3行+3行=6行,內容高度大於視圖高度,從而顯示不全。
再回到最初的問題,若要把內容顯示全,需要滿足兩個條件:
1,第一個格子的高度,是所有格子高度的最大值;
2,每排的最後一個格子的高度,是這一排中所有格子高度的最大值;
對於每個格子高度都一樣的,這兩個條件完全滿足。
但是字符串的長度不確定,從而導致了格子的高度不確定,怎麼辦呢?
我的辦法是,先全部遍歷一遍所有格子,找出內容最多的,放在第一個格子裡,通過系統的3次嘗試,獲取出高度最大值來。對於字符串而言,自然是最長的字符串換行最多,從而有最大的高度值。然後將所有格子的高度統一設置為這個值。當然,計算完成後,將第一個格子的內容也恢復為正常內容。
關鍵代碼如下:
package com.example.gridviewdemo; import java.util.List; import android.content.Context; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.GridView; import android.widget.TextView; public class FruitAdapter extends ArrayAdapter{ private int resoureId; GridView mGv; static int heightTextView=0; static int numFlushTextView=0; public FruitAdapter (Context context, GridView gv, int textViewResourceId, List objects ) { super(context, textViewResourceId,objects); resoureId=textViewResourceId; this.mGv = gv; Log.i("FruitAdapter", "log: consturctor: resoureId=" + resoureId); } @Override public View getView(int position, View convertView, ViewGroup parent) { Fruit fruit = getItem(position); View view; ViewHolder viewHolder; TextView tv_record ; String strLog; if(convertView == null){ view = LayoutInflater.from(getContext()).inflate(resoureId, null); strLog = "log: new ViewHolder "; viewHolder = new ViewHolder(); viewHolder.fruitName = (TextView) view.findViewById(R.id.fruit_name); view.setTag(viewHolder); viewHolder.fruitName.setText(fruit.getName()); tv_record = viewHolder.fruitName; Log.i("FruitAdapter", strLog+" pos="+position+" fruitName=" + fruit.getName()+" mGv.getHeight="+mGv.getHeight()); Log.i("FruitAdapter", "log: getView: lines="+tv_record.getLineCount() +" LineHeight="+ tv_record.getLineHeight()+" tv.height="+tv_record.getHeight()); if(heightTextView>0) { Log.i("adapter","to SetHeight="+heightTextView); tv_record.setHeight(heightTextView); } //只在第一次的時候,獲取最長字符串,並進行設置。以便於在後面進入時,獲取到系統測算的實際布局高度值 if((position==0)&&(numFlushTextView==0)) { int posForMaxLen=0; int maxLength=0; for(int i=0;i 0){ int numRows=mGv.getCount()/mGv.getNumColumns(); heightTextView = height/numRows; if(position==0){ tv_record.setText(getItem(0).getName().toString());//將內容改回原本應該使用的內容 tv_record.setHeight(heightTextView); numFlushTextView++; Log.i(" fruitadapter","log:="" heightview="+heightTextView+" numflush="+numFlushTextView); } } } return view; } class ViewHolder{ TextView fruitName; } }
最終效果如下:
後記:
現在其實還有個問題,就是若某一排中所有格子的內容都比較少,例如只有2行的高度,而GridView按最大高度值計算,給它安排的是3行的高度,就會有一行的空白了。
所以對於第一排之外的其他排,應該計算該排中內容最多的那個格子的高度值,用來設置該排最後一個格子的高度。
但是,Android系統只對第一排的第一個格子進行了3次計算,其他排的格子沒有這種計算,所以用這種方法就實現不了。
若有高人有方法實現這種精准的排列,請留言告訴我,萬分感謝!
在前一篇文章中(一步一步教你在 Android 裡創建自己的賬號系統(一)),我向大家介紹了如何在 Android 系統中創建自己的賬戶系統,
顯示操作進度的對話框 1、使用上一篇創建的同一項目,在activity_main.xml文件中添加一個Button: 2、在MainActivity.ja
Android Http 客戶端編程之GET 說起Http編程,不盡然想起GET和POST兩種請求方式,本文以簡潔明了的的步驟和說明,將Android中常用
一 、搭建Android Studio 安裝環境 二、安裝版本控制工具搭建好Android Studio後,開發過程中我們通常會用到版本控制工具來管理我們的代碼