背景:
- 谷歌建议避免使用嵌套的加权线性布局,因为这会影响性能。
- 使用嵌套的加权线性布局不易阅读、编写和维护。
- 至今仍没有很好的替代方法来放置大小为可用空间百分比的视图。唯一的解决方案是使用权重和OpenGL。甚至没有像WPF / Silverlight上显示的“viewBox”这样的自动缩放功能。
这就是我决定创建自己的布局的原因,您可以告诉它的每个子项相对于其大小应该有多大的权重(以及周围的权重)。
看起来我已经成功了,但由于某些原因,我认为存在一些无法追踪的错误。
其中一个错误是textView,即使我给了它很多空间,它也会将文本放在顶部而不是中心位置。另一方面,imageViews工作得非常好。另一个错误是,如果我在我的自定义布局中使用布局(例如frameLayout),则其中的视图将不会显示(但布局本身将会显示)。
请帮我弄清楚它为什么发生。
如何使用:与线性布局的下一个用法相比(我故意使用了长XML,以显示我的解决方案如何缩短事物):
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent" android:orientation="vertical">
<View android:layout_width="wrap_content" android:layout_height="0px"
android:layout_weight="1" />
<LinearLayout android:layout_width="match_parent"
android:layout_height="0px" android:layout_weight="1"
android:orientation="horizontal">
<View android:layout_width="0px" android:layout_height="wrap_content"
android:layout_weight="1" />
<TextView android:layout_width="0px" android:layout_weight="1"
android:layout_height="match_parent" android:text="@string/hello_world"
android:background="#ffff0000" android:gravity="center"
android:textSize="20dp" android:textColor="#ff000000" />
<View android:layout_width="0px" android:layout_height="wrap_content"
android:layout_weight="1" />
</LinearLayout>
<View android:layout_width="wrap_content" android:layout_height="0px"
android:layout_weight="1" />
</LinearLayout>
我的工作只是将视图本身放在权重列表中的x位置:
<com.example.weightedlayouttest.WeightedLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res/com.example.weightedlayouttest"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent" tools:context=".MainActivity">
<TextView android:layout_width="0px" android:layout_height="0px"
app:horizontalWeights="1,1x,1" app:verticalWeights="1,1x,1"
android:text="@string/hello_world" android:background="#ffff0000"
android:gravity="center" android:textSize="20dp" android:textColor="#ff000000" />
</com.example.weightedlayouttest.WeightedLayout>
我特殊布局的代码如下:
public class WeightedLayout extends ViewGroup
{
@Override
protected WeightedLayout.LayoutParams generateDefaultLayoutParams()
{
return new WeightedLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,ViewGroup.LayoutParams.WRAP_CONTENT);
}
@Override
public WeightedLayout.LayoutParams generateLayoutParams(final AttributeSet attrs)
{
return new WeightedLayout.LayoutParams(getContext(),attrs);
}
@Override
protected ViewGroup.LayoutParams generateLayoutParams(final android.view.ViewGroup.LayoutParams p)
{
return new WeightedLayout.LayoutParams(p.width,p.height);
}
@Override
protected boolean checkLayoutParams(final android.view.ViewGroup.LayoutParams p)
{
final boolean isCorrectInstance=p instanceof WeightedLayout.LayoutParams;
return isCorrectInstance;
}
public WeightedLayout(final Context context)
{
super(context);
}
public WeightedLayout(final Context context,final AttributeSet attrs)
{
super(context,attrs);
}
public WeightedLayout(final Context context,final AttributeSet attrs,final int defStyle)
{
super(context,attrs,defStyle);
}
@Override
protected void onLayout(final boolean changed,final int l,final int t,final int r,final int b)
{
for(int i=0;i<this.getChildCount();++i)
{
final View v=getChildAt(i);
final WeightedLayout.LayoutParams layoutParams=(WeightedLayout.LayoutParams)v.getLayoutParams();
//
final int availableWidth=r-l;
final int totalHorizontalWeights=layoutParams.getLeftHorizontalWeight()+layoutParams.getViewHorizontalWeight()+layoutParams.getRightHorizontalWeight();
final int left=l+layoutParams.getLeftHorizontalWeight()*availableWidth/totalHorizontalWeights;
final int right=r-layoutParams.getRightHorizontalWeight()*availableWidth/totalHorizontalWeights;
//
final int availableHeight=b-t;
final int totalVerticalWeights=layoutParams.getTopVerticalWeight()+layoutParams.getViewVerticalWeight()+layoutParams.getBottomVerticalWeight();
final int top=t+layoutParams.getTopVerticalWeight()*availableHeight/totalVerticalWeights;
final int bottom=b-layoutParams.getBottomVerticalWeight()*availableHeight/totalVerticalWeights;
//
v.layout(left+getPaddingLeft(),top+getPaddingTop(),right+getPaddingRight(),bottom+getPaddingBottom());
}
}
// ///////////////
// LayoutParams //
// ///////////////
public static class LayoutParams extends ViewGroup.LayoutParams
{
int _leftHorizontalWeight =0,_rightHorizontalWeight=0,_viewHorizontalWeight=0;
int _topVerticalWeight =0,_bottomVerticalWeight=0,_viewVerticalWeight=0;
public LayoutParams(final Context context,final AttributeSet attrs)
{
super(context,attrs);
final TypedArray arr=context.obtainStyledAttributes(attrs,R.styleable.WeightedLayout_LayoutParams);
{
final String horizontalWeights=arr.getString(R.styleable.WeightedLayout_LayoutParams_horizontalWeights);
//
// handle horizontal weight:
//
final String[] words=horizontalWeights.split(",");
boolean foundViewHorizontalWeight=false;
int weight;
for(final String word : words)
{
final int viewWeightIndex=word.lastIndexOf('x');
if(viewWeightIndex>=0)
{
if(foundViewHorizontalWeight)
throw new IllegalArgumentException("found more than one weights for the current view");
weight=Integer.parseInt(word.substring(0,viewWeightIndex));
setViewHorizontalWeight(weight);
foundViewHorizontalWeight=true;
}
else
{
weight=Integer.parseInt(word);
if(weight<0)
throw new IllegalArgumentException("found negative weight:"+weight);
if(foundViewHorizontalWeight)
_rightHorizontalWeight+=weight;
else _leftHorizontalWeight+=weight;
}
}
if(!foundViewHorizontalWeight)
throw new IllegalArgumentException("couldn't find any weight for the current view. mark it with 'x' next to the weight value");
}
//
// handle vertical weight:
//
{
final String verticalWeights=arr.getString(R.styleable.WeightedLayout_LayoutParams_verticalWeights);
final String[] words=verticalWeights.split(",");
boolean foundViewVerticalWeight=false;
int weight;
for(final String word : words)
{
final int viewWeightIndex=word.lastIndexOf('x');
if(viewWeightIndex>=0)
{
if(foundViewVerticalWeight)
throw new IllegalArgumentException("found more than one weights for the current view");
weight=Integer.parseInt(word.substring(0,viewWeightIndex));
setViewVerticalWeight(weight);
foundViewVerticalWeight=true;
}
else
{
weight=Integer.parseInt(word);
if(weight<0)
throw new IllegalArgumentException("found negative weight:"+weight);
if(foundViewVerticalWeight)
_bottomVerticalWeight+=weight;
else _topVerticalWeight+=weight;
}
}
if(!foundViewVerticalWeight)
throw new IllegalArgumentException("couldn't find any weight for the current view. mark it with 'x' next to the weight value");
}
//
arr.recycle();
}
public LayoutParams(final int width,final int height)
{
super(width,height);
}
public LayoutParams(final ViewGroup.LayoutParams source)
{
super(source);
}
public int getLeftHorizontalWeight()
{
return _leftHorizontalWeight;
}
public void setLeftHorizontalWeight(final int leftHorizontalWeight)
{
_leftHorizontalWeight=leftHorizontalWeight;
}
public int getRightHorizontalWeight()
{
return _rightHorizontalWeight;
}
public void setRightHorizontalWeight(final int rightHorizontalWeight)
{
if(rightHorizontalWeight<0)
throw new IllegalArgumentException("negative weight :"+rightHorizontalWeight);
_rightHorizontalWeight=rightHorizontalWeight;
}
public int getViewHorizontalWeight()
{
return _viewHorizontalWeight;
}
public void setViewHorizontalWeight(final int viewHorizontalWeight)
{
if(viewHorizontalWeight<0)
throw new IllegalArgumentException("negative weight:"+viewHorizontalWeight);
_viewHorizontalWeight=viewHorizontalWeight;
}
public int getTopVerticalWeight()
{
return _topVerticalWeight;
}
public void setTopVerticalWeight(final int topVerticalWeight)
{
if(topVerticalWeight<0)
throw new IllegalArgumentException("negative weight :"+topVerticalWeight);
_topVerticalWeight=topVerticalWeight;
}
public int getBottomVerticalWeight()
{
return _bottomVerticalWeight;
}
public void setBottomVerticalWeight(final int bottomVerticalWeight)
{
if(bottomVerticalWeight<0)
throw new IllegalArgumentException("negative weight :"+bottomVerticalWeight);
_bottomVerticalWeight=bottomVerticalWeight;
}
public int getViewVerticalWeight()
{
return _viewVerticalWeight;
}
public void setViewVerticalWeight(final int viewVerticalWeight)
{
if(viewVerticalWeight<0)
throw new IllegalArgumentException("negative weight :"+viewVerticalWeight);
_viewVerticalWeight=viewVerticalWeight;
}
}
}