【问题标题】:Button Layout with Profile Picture Alignment带有配置文件图片对齐的按钮布局
【发布时间】:2015-02-08 05:29:37
【问题描述】:

我想创建如图所示的按钮。在圆圈内(在 png 中是透明的)我想放置玩家的头像。蓝条上也应该有文字。 我已经让它工作了,但它似乎太复杂了。我认为在不提供代码的情况下更容易理解我所做的事情,但是如果您需要它,我可以添加它。这是布局:

  • 相对布局
    • 线性布局(水平方向)
      • 重量为 0.7 的空视图
      • 重量为 0.2 的个人资料图片
      • 权重为 0.1 的空视图
    • 我贴在下面的叠加图
    • 线性布局(水平方向)
      • 权重为 0.7 的相对布局(所有文本可以放置的空间)
      • 重量为 0.3 的空视图

顺便说一句:圆圈右边的png不是透明的,而是白色的!

这很好,但必须有更好的方法!所有这些空视图只是为了将图片对齐到正确的位置,这有点难看。而且覆盖图必须介于个人资料图片和文字之间的事实使它更加难看。

我更喜欢不使用 png 作为叠加层,而是使用简单的形状(这样它在每个屏幕上看起来都不错),但我不知道该怎么做。你会推荐吗?如果是,那怎么做?

或者您知道如何改进 xml 布局或其他方式。

非常感谢

【问题讨论】:

  • 今天你将看到解决方案。等等。

标签: android xml layout


【解决方案1】:

你可以在没有任何图像的情况下做到这一点:

布局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:weightSum="1.0">
    <TextView
        android:layout_weight="0.7"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:text="New Text"
        android:id="@+id/textView"
        android:background="#0073ff"/>
    <ImageView
        android:layout_weight="0.2"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:background="@drawable/half_round_drawable"
        android:src="@drawable/profile"/>
    </LinearLayout>
</LinearLayout>

half_round_drawable:

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >

    <item>
        <shape android:shape="oval">
            <corners android:radius="16dp" />
            <solid android:color="#0073ff" />
        </shape>
    </item>
    <item
        android:bottom="0dp"
        android:right="32dp"> <!-- radius *2 -->
        <shape>
            <solid android:color="#0073ff" />
        </shape>
    </item>

</layer-list>

要制作个人资料图片,您应该使用以下内容: How to create a circular ImageView in Android?

【讨论】:

    【解决方案2】:

    如果您将背景图像限制在右侧的配置文件区域,则可以使用简单的LinearLayout。如果您使用九补丁可绘制对象,则可以在图像本身中定义内容区域,如下所示:

    1. 从背景图像文件中提取配置文件部分。
    2. 从中创建一个可绘制的九个补丁,将所有区域定义为可拉伸区域(左侧和顶部边框线),并将空圆圈定义为内容区域(右侧和底部线条)。
    3. 由于理想情况下您应该将图像放在前景层以确保照片不会绘制在圆圈之外,您可以使用带有前景可绘制对象的FrameLayout 来包含您的个人资料照片的ImageView。还需要另一个虚拟子视图来解决FrameLayout 中的bug,这会导致具有match_parent 尺寸的单个子视图布局不正确。

    最后的布局应该是这样的:

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
    
        <TextView
            android:id="@+id/text"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:background="#00f" />
    
        <FrameLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:foreground="@drawable/profile_bg">
    
            <TextView
                android:layout_width="match_parent"
                android:layout_height="match_parent" />
    
            <ImageView
                android:id="@+id/photo"
                android:layout_width="match_parent"
                android:layout_height="match_parent" />
        </FrameLayout>
    </LinearLayout>
    

    【讨论】:

    • 如果我正确理解了所有内容,这正是我所寻找的。请问你对恩恩的回答有什么看法,这似乎也很好。该解决方案的唯一缺点是它的形状不像 9 个补丁那样灵活吗?因此,如果我 100% 确定我的设计始终保持如此简单以至于可以用形状创建,我会采用他的解决方案吗?
    • @DominicM:Eun 的解决方案也可以工作,在可绘制形状中定义大小和填充。在这种情况下,圆圈不会是透明的,因此前景技巧在那里不起作用,但正如他建议的那样,您可以使用RoundedImageView。但是,如果从默认尺寸缩放,它将无法正确绘制。如果这是一个问题,它仍然可以通过使用 clip 可绘制而不是硬编码第二个形状的宽度/插图并将可绘制级别从代码设置为 5000 (50%) 来解决。
    • @DominicM:另一种选择是在布局本身中执行此操作,方法是在TextView 的右侧添加负边距和填充,等于圆的半径。结合定义了适当边框的RoundedImageView,这也应该产生所需的结果。这也将受到与 shape 可绘制解决方案相同的限制。
    【解决方案3】:

    现在我准备提出我的答案。

    • 波特雷特:

    • 风景:

    布局.xml:

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:shape="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin"
        android:paddingBottom="@dimen/activity_vertical_margin"
        tools:context=".MainActivity">
    
        <!--This is the CustomView which include -->
        <!--own attributes: -->
        <!--circle_radius is the radius of image, -->
        <!--content_padding is the padding,-->
        <!--and background_color is the color of shape.-->
    
        <CustomShape
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            shape:circle_radius="40dp"
            shape:content_padding="8dp"
            shape:background_color="#FF983493">
    
            <!--There must be two Views:-->
            <!--TextView and ImageView and only in this order.-->
            <!--Set-->
            <!--android:layout_width="wrap_content"-->
            <!--android:layout_height="wrap_content"-->
            <!--to bot of them, because in CustomShape it will be-->
            <!--resized for you. There also don`t need to set -->
            <!--any kind of margin or location attributes.-->
    
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:id="@+id/txt"
                android:padding="5dp"
                android:textColor="@android:color/white"
                android:text="sdsfkjsdkfhsdk flsdkfjkls asdfasd fklasdjl fkjasdklfjasd k "
                android:background="@android:color/transparent"/>
    
            <!--For RoundImage I use custom class which round the drawable,-->
            <!--not a View. Look down.-->
    
            <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:id="@+id/img"
                android:src="@drawable/img"
                android:scaleType="fitCenter"
                android:background="@android:color/transparent" />
    
        </CustomShape>
    
    </RelativeLayout>
    

    CustomShape 类:

    public class CustomShape extends RelativeLayout {
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
    
        int circleRadius; // image radius
        int diameter; // image diameter
        int contentPadding;
        int semiPadding;
        int rectRightSide;
    
        int backgroundColor;
        int viewWidth; // width of parent(CustomShape layout)
    
        public CustomShape(Context context) {
            super(context);
            this.setWillNotDraw(false);
        }
    
        public CustomShape(Context context, AttributeSet attrs) {
            super(context, attrs);
    
            TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.CustomShape, 0, 0);
            try {
                this.circleRadius = (int) ta.getDimension(R.styleable.CustomShape_circle_radius, 40);
                this.contentPadding = (int) ta.getDimension(R.styleable.CustomShape_content_padding, 8);
                this.backgroundColor = ta.getColor(R.styleable.CustomShape_background_color, 0);
    
                this.semiPadding = contentPadding / 2;
                this.diameter = circleRadius * 2;
            } finally {
                ta.recycle();
            }
    
            this.setWillNotDraw(false);
        }
    
        public CustomShape(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            this.setWillNotDraw(false);
        }
    
        @Override
        protected void onSizeChanged(int xNew, int yNew, int xOld, int yOld) {
            super.onSizeChanged(xNew, yNew, xOld, yOld);
    
            viewWidth = xNew;
    
            this.rectRightSide = viewWidth - circleRadius - (circleRadius / 2); // get position for image
        }
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            ImageView img = (ImageView) this.getChildAt(1);
            RelativeLayout.LayoutParams imgParams = new LayoutParams(diameter - contentPadding, diameter - contentPadding);
            imgParams.leftMargin = rectRightSide - circleRadius + semiPadding;
            imgParams.topMargin = semiPadding;
            img.setLayoutParams(imgParams);
    
            //Create custom RoundImage and set to image
    
            try {
                Drawable drawable = img.getDrawable();
                Bitmap bm = ((BitmapDrawable) drawable).getBitmap();
                RoundImage resultImage = new RoundImage(bm);
                img.setImageDrawable(resultImage);
            } catch (ClassCastException e) {
            }
    
            //Positioning and resizing TextView
    
            View txt = this.getChildAt(0);
            RelativeLayout.LayoutParams txtParams = new LayoutParams(rectRightSide - circleRadius - semiPadding, diameter - contentPadding);
            txtParams.topMargin = semiPadding;
            txtParams.leftMargin = semiPadding;
            txt.setLayoutParams(txtParams);
    
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
    
            this.setMeasuredDimension(parentWidth, diameter); // set correct height
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
    
            paint.setColor(backgroundColor);
            canvas.drawRect(0, 0, rectRightSide, diameter, paint);
    
            //Draw circle
    
            paint.setDither(true);
            canvas.drawCircle(rectRightSide, circleRadius, circleRadius, paint);
        }
    }
    

    属性.xml:

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <declare-styleable name="CustomShape">
            <attr name="circle_radius" format="dimension" />
            <attr name="content_padding" format="dimension" />
            <attr name="background_color" format="color" />
        </declare-styleable>
    </resources>
    

    圆形图像类:

    public class RoundImage extends Drawable {
        private final Bitmap mBitmap;
        private final Paint mPaint;
        private final RectF mRectF;
        private final int mBitmapWidth;
        private final int mBitmapHeight;
    
        public RoundImage(Bitmap bitmap) {
            mBitmap = bitmap;
            mRectF = new RectF();
            mPaint = new Paint();
            mPaint.setAntiAlias(true);
            mPaint.setDither(true);
            final BitmapShader shader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
            mPaint.setShader(shader);
    
            mBitmapWidth = mBitmap.getWidth();
            mBitmapHeight = mBitmap.getHeight();
        }
    
        @Override
        public void draw(Canvas canvas) {
            canvas.drawOval(mRectF, mPaint);
        }
    
        @Override
        protected void onBoundsChange(Rect bounds) {
            super.onBoundsChange(bounds);
            mRectF.set(bounds);
        }
    
        @Override
        public void setAlpha(int alpha) {
            if (mPaint.getAlpha() != alpha) {
                mPaint.setAlpha(alpha);
                invalidateSelf();
            }
        }
    
        @Override
        public void setColorFilter(ColorFilter cf) {
            mPaint.setColorFilter(cf);
        }
    
        @Override
        public int getOpacity() {
            return PixelFormat.TRANSLUCENT;
        }
    
        @Override
        public int getIntrinsicWidth() {
            return mBitmapWidth;
        }
    
        @Override
        public int getIntrinsicHeight() {
            return mBitmapHeight;
        }
    
        public void setAntiAlias(boolean aa) {
            mPaint.setAntiAlias(aa);
            invalidateSelf();
        }
    
        @Override
        public void setFilterBitmap(boolean filter) {
            mPaint.setFilterBitmap(filter);
            invalidateSelf();
        }
    
        @Override
        public void setDither(boolean dither) {
            mPaint.setDither(dither);
            invalidateSelf();
        }
    
        public Bitmap getBitmap() {
            return mBitmap;
        }
    
    }
    

    希望对你有所帮助。

    【讨论】:

    • 如果您使用的是自定义视图,那么您不妨让它自己实现整个绘图逻辑,而不是在标准容器之上硬编码一些调整。您定义的所有三个属性都已经存在或可以从现有属性中推断出来。另外,你为什么要从onDraw()方法修改布局属性,然后调用requestLayout()?这将导致绘制和布局的无限循环。
    • 如果 blabla 我可以创建。这是样本。为什么是 requestLayout,因为这个视图在附加到布局时几乎没有移动。不幸的是,这是错误的答案。
    • 但是为什么要从onDraw() 内部更改布局?理想情况下,这应该移至onMeasure()onDraw() 方法应该是轻量级的并且没有副作用,正如我所提到的,从那里调用 requestLayout() 会导致无限循环。
    • 我知道。但是当我试图将其放入onMeasure() 时,什么也没发生。
    • 您需要在调用onMeasure() 的超级实现之前更改布局参数 以产生任何效果。
    猜你喜欢
    • 2012-07-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-05
    • 1970-01-01
    相关资源
    最近更新 更多