天天看點

自定義TextView,解決文字換行,\n換行符換行,導緻高度計算不對問題(計算ListView高度)

剛遇到一個問題,如題,我有2個ListView嵌套,是以需要計算嵌套中的ListView高度,然後就發現,TextView 中的文字換行會導緻高度計算有誤,隻算到一行,So,上網查解決方法,找到的方法都是一個

public class myTextView extends TextView {

   private Context context;
   public myTextView(Context context) {
      super(context);
      // TODO Auto-generated constructor stub
      this.context = context;
   }
   public myTextView(Context context, AttributeSet attrs) {
      super(context, attrs);
      // TODO Auto-generated constructor stub
      this.context = context;
   }
   public myTextView(Context context, AttributeSet attrs, int defStyle) {
      super(context, attrs, defStyle);
      // TODO Auto-generated constructor stub
      this.context = context;
   }


   @Override
   protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
       super.onMeasure(widthMeasureSpec, heightMeasureSpec);

       Layout layout = getLayout();
       if (layout != null) {
           int height = (int)FloatMath.ceil(getMaxLineHeight(this.getText().toString()))
                   + getCompoundPaddingTop() + getCompoundPaddingBottom();
           int width = getMeasuredWidth();            
           setMeasuredDimension(width, height);
       }
   }

   private float getMaxLineHeight(String str) {
      float height = f;
      float screenW = ((Activity)context).getWindowManager().getDefaultDisplay().getWidth();
      float paddingLeft = ((LinearLayout)this.getParent()).getPaddingLeft();
      float paddingReft = ((LinearLayout)this.getParent()).getPaddingRight();
//這裡具體this.getPaint()要注意使用,要看你的TextView在什麼位置,這個是拿TextView父控件的Padding的,為了更準确的算出換行
 int line = (int) Math.ceil( (this.getPaint().measureText(str)/(screenW-paddingLeft-paddingReft))); 
 height = (this.getPaint().getFontMetrics().descent-this.getPaint().getFontMetrics().ascent)*line; return height;}
}
           

基本找到所有資料都是以上的代碼,并不知道原作者到底是誰,自定義TextView計算高度,但是我用了之後發現有幾個問題

一、當TextView沒有占據一行時,顯示會不全,也就是TextView左邊或者右邊還有控件占據了寬度,那麼就會有部分資料不顯示

二、沒有考慮文本中含有\n換行符,計算行數有誤

三、沒有計算TextView本身的Padding

四、擷取螢幕密度方式有誤,使用以下方法強轉是有風險的,且方法已經過時

float screenW = ((Activity)context).getWindowManager().getDefaultDisplay().getWidth();
      float paddingLeft = 
           

解決辦法就是當你的TextView旁邊還有其他控件占據寬度時,把TextView的寬度定死,或使用weight,是TextView與其他View按比例配置設定寬度。

修改後的版本

public class MTextView extends TextView {

    private Context context;

    public MTextView(Context context) {
        super(context);
        // TODO Auto-generated constructor stub
        this.context = context;
    }

    public MTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        // TODO Auto-generated constructor stub
        this.context = context;
    }

    public MTextView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        // TODO Auto-generated constructor stub
        this.context = context;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int mode = MeasureSpec.getMode(heightMeasureSpec);
        Layout layout = getLayout();
        if (layout != null) {
            int height = (int) Math.ceil(getMaxLineHeight(this.getText().toString(), mode))
                    + getCompoundPaddingTop() + getCompoundPaddingBottom();
            int width = getMeasuredWidth();
            setMeasuredDimension(width, height);
        }
    }

    private float getMaxLineHeight(String str, int mode) {
        float height = f;
        float width = getMeasuredWidth();
        float widthPixels = context.getResources().getDisplayMetrics().widthPixels;
        //這裡具體this.getPaint()要注意使用,要看你的TextView在什麼位置,
        // 這個是拿TextView父控件的Padding的,為了更準确的算出換行
        float pLeft = ((LinearLayout) getParent()).getPaddingLeft();
        float pRight = ((LinearLayout) getParent()).getPaddingRight();
        //檢測字元串中是否包含換行符,獲得換行的次數,在之後計算高度時加上
        int br = ;
        if (str.contains("\n"))
            br = str.split("\n").length - ;
        /**
         *  wrap_content/未指定寬度(MeasureSpec.UNSPECIFIED),則用螢幕寬度計算
         *  否則就使用View自身寬度計算,并且無需計算Parent的Padding
         */
        int line;
        if (mode == MeasureSpec.UNSPECIFIED)
            line = (int)
                    Math.ceil((this.getPaint().measureText(str) /
                            (widthPixels - getPaddingLeft() - pLeft - pRight - getPaddingRight())));
        else {
            line = (int)
                    Math.ceil((this.getPaint().measureText(str) /
                            (width - getPaddingLeft() - getPaddingRight())));
        }
        height = (this.getPaint().getFontMetrics().descent -
                this.getPaint().getFontMetrics().ascent) * (line + br);
        return height;
    }
}
           

如果有更好的解決辦法,或代碼中有忽略的問題,bug請留言。

另,可能有人會問RelativeLayout怎麼辦,我想說,計算ListView高度如果有RelativeLayout,那麼是計算不了的,估計隻能再次重寫RelativeLayout…

繼續閱讀