我接受了您的挑战并尝试创建您描述的布局以回应我的评论。你是对的。实现起来出人意料地困难。除此之外,我确实喜欢拍摄家蝇。所以我加入并想出了这个解决方案。
扩展现有的布局类,而不是从头开始创建自己的布局类。我一开始就使用了RelativeLayout,但是所有这些都可以使用相同的方法。这使您能够在您不想操作的子视图上使用该布局的默认行为。
我向布局添加了四个属性,称为顶部、左侧、宽度和高度。我的目的是通过允许“10%”、“100px”、“100dp”等值来模仿 HTML。此时唯一接受的值是表示父级百分比的整数。 “20”= 布局的 20%。
为了获得更好的性能,我允许 super.onLayout() 执行所有迭代,并且仅在最后一次传递时使用自定义属性来操作视图。由于这些视图将独立于兄弟姐妹进行定位和缩放,因此我们可以在其他一切都解决后移动它们。
这是 atts.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="HtmlStyleLayout">
<attr name="top" format="integer"/>
<attr name="left" format="integer"/>
<attr name="height" format="integer"/>
<attr name="width" format="integer"/>
</declare-styleable>
</resources>
这是我的布局类。
package com.example.helpso;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.RelativeLayout;
public class HtmlStyleLayout extends RelativeLayout{
private int pass =0;
@Override
protected HtmlStyleLayout.LayoutParams generateDefaultLayoutParams()
{
return new HtmlStyleLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT,RelativeLayout.LayoutParams.WRAP_CONTENT);
}
@Override
public HtmlStyleLayout.LayoutParams generateLayoutParams(final AttributeSet attrs)
{
return new HtmlStyleLayout.LayoutParams(getContext(),attrs);
}
@Override
protected RelativeLayout.LayoutParams generateLayoutParams(final android.view.ViewGroup.LayoutParams p)
{
return new HtmlStyleLayout.LayoutParams(p.width,p.height);
}
@Override
protected boolean checkLayoutParams(final android.view.ViewGroup.LayoutParams p)
{
final boolean isCorrectInstance=p instanceof HtmlStyleLayout.LayoutParams;
return isCorrectInstance;
}
public HtmlStyleLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void setScaleType(View v){
try{
((ImageView) v).setScaleType (ImageView.ScaleType.FIT_XY);
}catch (Exception e){
// The view is not an ImageView
}
}
@Override
protected void onLayout(final boolean changed,final int l,final int t,final int r,final int b)
{
super.onLayout(changed, l, t, r, b); //Let the parent layout do it's thing
pass++; // After the last pass of
final int childCount = this.getChildCount(); // the parent layout
if(true){ // we do our thing
for(int i=0;i<childCount;++i)
{
final View v=getChildAt(i);
final HtmlStyleLayout.LayoutParams params = (HtmlStyleLayout.LayoutParams)v.getLayoutParams();
int newTop = v.getTop(); // set the default value
int newLeft = v.getLeft(); // of these to the value
int newBottom = v.getBottom(); // set by super.onLayout()
int newRight= v.getRight();
boolean viewChanged = false;
if(params.getTop() >= 0){
newTop = ( (int) ((b-t) * (params.getTop() * .01)) );
viewChanged = true;
}
if(params.getLeft() >= 0){
newLeft = ( (int) ((r-l) * (params.getLeft() * .01)) );
viewChanged = true;
}
if(params.getHeight() > 0){
newBottom = ( (int) ((int) newTop + ((b-t) * (params.getHeight() * .01))) );
setScaleType(v); // set the scale type to fitxy
viewChanged = true;
}else{
newBottom = (newTop + (v.getBottom() - v.getTop()));
Log.i("heightElse","v.getBottom()=" +
Integer.toString(v.getBottom())
+ " v.getTop=" +
Integer.toString(v.getTop()));
}
if(params.getWidth() > 0){
newRight = ( (int) ((int) newLeft + ((r-l) * (params.getWidth() * .01))) );
setScaleType(v);
viewChanged = true;
}else{
newRight = (newLeft + (v.getRight() - v.getLeft()));
}
// only call layout() if we changed something
if(viewChanged)
Log.i("SizeLocation",
Integer.toString(i) + ": "
+ Integer.toString(newLeft) + ", "
+ Integer.toString(newTop) + ", "
+ Integer.toString(newRight) + ", "
+ Integer.toString(newBottom));
v.layout(newLeft, newTop, newRight, newBottom);
}
pass = 0; // reset the parent pass counter
}
}
public class LayoutParams extends RelativeLayout.LayoutParams
{
private int top, left, width, height;
public LayoutParams(final Context context, final AttributeSet atts) {
super(context, atts);
TypedArray a = context.obtainStyledAttributes(atts, R.styleable.HtmlStyleLayout);
top = a.getInt(R.styleable.HtmlStyleLayout_top , -1);
left = a.getInt(R.styleable.HtmlStyleLayout_left, -1);
width = a.getInt(R.styleable.HtmlStyleLayout_width, -1);
height = a.getInt(R.styleable.HtmlStyleLayout_height, -1);
a.recycle();
}
public LayoutParams(int w, int h) {
super(w,h);
Log.d("lp","2");
}
public LayoutParams(ViewGroup.LayoutParams source) {
super(source);
Log.d("lp","3");
}
public LayoutParams(ViewGroup.MarginLayoutParams source) {
super(source);
Log.d("lp","4");
}
public int getTop(){
return top;
}
public int getLeft(){
return left;
}
public int getWidth(){
return width;
}
public int getHeight(){
return height;
}
}
}
这是一个活动 xml 示例
<com.example.helpso.HtmlStyleLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:html="http://schemas.android.com/apk/res/com.example.helpso"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ImageView
android:id="@+id/imageView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:scaleType="fitXY"
android:src="@drawable/bg" />
<ImageView
android:id="@+id/imageView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/overlay"
html:height="10"
html:left="13"
html:top="18"
html:width="23" />
</com.example.helpso.HtmlStyleLayout>
这是我用于测试的图像。
如果您没有为特定属性设置值,则将使用默认值。因此,如果您设置宽度而不是高度,则图像将按宽度缩放,并按高度换行内容。
压缩的项目文件夹。 https://docs.google.com/open?id=0B2BD9i1zpmm5QVpVcGN6NTNHQ2s
apk https://docs.google.com/open?id=0B2BD9i1zpmm5LWo4d3JCR0VIRWM
我找到了错误的根源。问题是我使用布局的子计数来指示它将对 onLayout 进行多少次调用。这在旧版本的 Android 中似乎并不适用。我注意到在 2.1 中 onLayout 只被调用一次。所以我改变了
if(pass == childCount){
to
if(true){
它开始按预期工作。
我仍然认为只有在 super 完成之后才调整布局是有益的。只需找到一种更好的方法来知道那是什么时候。
EDIT
我没有意识到您的意图是以像素为单位的精度将图像拼凑在一起。我通过使用双浮点精度变量而不是整数来实现您正在寻找的精度。但是,在允许图像缩放的同时,您将无法实现此目的。放大图像时,会在现有像素之间以一定间隔添加像素。新像素的颜色是周围像素的加权平均值。当您独立缩放图像时,它们不会共享任何信息。结果是你总是会在接缝处出现一些伪影。添加四舍五入的结果,因为您不能有部分像素,并且您将始终具有 +/-1 像素容差。
要验证这一点,您可以在高级照片编辑软件中尝试执行相同的任务。我使用 PhotoShop。我使用与我的 apk 中相同的图像,将它们放在单独的文件中。我将它们垂直缩放了 168%,水平缩放了 127%。然后我将它们放在一个文件中并尝试对齐它们。结果与我的 apk 中看到的完全一样。
为了演示布局的准确性,我向我的 apk 添加了第二个活动。在此活动中,我没有缩放背景图像。其他一切都完全相同。结果是无缝的。
我还添加了一个用于显示/隐藏覆盖图像的按钮和一个用于在活动之间切换的按钮。
我更新了我的谷歌驱动器上的 apk 和压缩项目文件夹。您可以通过上面的链接获取它们。