Android 计算位图平铺高度,使最后一项不被裁剪

Android 计算位图平铺高度,使最后一项不被裁剪,android,android-layout,bitmap,android-custom-view,Android,Android Layout,Bitmap,Android Custom View,问题是要创建平铺背景,该背景将以不裁剪最后一个元素的方式拉伸X轴 我想要的内容(计算每个“点”图像的高度,这样最后一块瓷砖就不会被裁剪): 我拥有的(见最后一个元素被裁剪): 应该有一种方法来创建扩展ImageView或Bitmap的自定义类,并手动实现该计算。但我真的无法得到任何关于如何正确地做到这一点的信息 bg_dots.xml: <?xml version="1.0" encoding="utf-8"?> <bitmap xmlns:android="htt

问题是要创建平铺背景,该背景将以不裁剪最后一个元素的方式拉伸X轴

我想要的内容(计算每个“点”图像的高度,这样最后一块瓷砖就不会被裁剪):

我拥有的(见最后一个元素被裁剪):

应该有一种方法来创建扩展
ImageView
Bitmap
的自定义类,并手动实现该计算。但我真的无法得到任何关于如何正确地做到这一点的信息

bg_dots.xml:

<?xml version="1.0" encoding="utf-8"?>
<bitmap
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:src="@drawable/ic_dot"
    android:tileMode="repeat"/>
请查看
calculateWidth()
方法

它看起来是什么样子(由于delta仍然有被裁剪的点):

我不知道如何通过
位图
/
xml
实现您想要的功能,但是(正如您所想)我可以建议创建自定义类

方法允许您在中定义自定义绘图过程和

您可以计算点的正确计数/高度

您可以使用绘制单点

然后你会得到一个定制的wiget,你可以把它放在一些布局容器中(比如
TextView
,而不是
android:background


我写了一个案例,描述了一个更复杂的案例,但是你可以看到上面提到的每个方法的用法。例如,可以跳过有关路径或属性的段落


这是一个很好的例子。这可能对您也很有用,但它非常通用。

如果您想在Android系统中进行自定义绘图,您应该只使用Android框架,测量和布局等工作将变得容易得多

return (int)(screenHeight * 0.07); //make it around 7 percents of screen
如果您在测量中“猜测”高度,则实际上并不是在测量视图。你需要25个点,沿着视图的宽度分布,所以这是你应该做的

  • 摆脱
    窗口管理器
    显示
    ,等等。Android现在有分屏模式,有软导航条……屏幕大小并不重要
  • 使用Android框架
  • 它总是一样的过程:测量、布局、绘制

    测量 你在测量中,所以测量
    widthMeasureSpec
    包含在本例中可用宽度所需的信息。用
    MeasureSpec.getSize(widthmasurespec)
    阅读它

    现在你需要沿着这个宽度有25个点,并且分别有一个高度……所以你所需要做的就是将
    宽度/25
    分开,你就知道你的视图应该有多高了

    通过告诉框架你想要多高来完成测量

    setMeasuredDimension(availableWidth, neededHeight);
    
    布局 如果发生了变化……不管是什么原因……这里我们得到了视图的最终大小。调整您的绘图,缩放图像,计算边界

    只是尽量不要做太复杂的工作,因为这可能会被称为很多

    绘画 您可以通过在画布上创建一些
    Paint
    和一些
    drawCircle(…)
    来进行完全自定义绘图,或者您可以使用一些
    Drawable
    。关于如何画一个点有无限的可能性

    您需要它25次,因此添加一个循环,并确保沿x轴移动它。现在你准备好了


    因为这是一个很难掌握的问题,这里有一个简单的例子,有一个基本的可绘制的。我还写了一篇关于测量和布局的文章。试着看看它是如何工作的(我自己喜欢在Android Studio中玩布局预览来测试我的视图!)。一旦您了解了如何测量和布局视图,剩下的就是正确缩放位图

    public class DotsView extends View {
        private static final int NUM_DOTS = 25;
    
        private Drawable mDrawable;
    
        public DotsView(Context context) {
            super(context);
            init();
        }
    
        public DotsView(Context context, AttributeSet attrs) {
            super(context, attrs);
            init();
        }
    
        public DotsView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            init();
        }
    
        private void init() {
            mDrawable = new DotDrawable();
        }
    
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            // measure where you're supposed to measure: in onMeasure
    
            // get all of the width
            // this will work with match_parent, as well as with a width of 32dp
            int availableWidth = MeasureSpec.getSize(widthMeasureSpec);
    
            // NUM_DOTS == 25, or whatever, use a constant or read into attributes
            int neededHeight = (int) (availableWidth / (float) NUM_DOTS);
    
            // apply the measure dimension to the view
            setMeasuredDimension(availableWidth, neededHeight);
        }
    
        @Override
        protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
            super.onLayout(changed, left, top, right, bottom);
    
            // called after measuring. here we know the actual size of our view
    
            // assuming it's a square, else do some Math.min to get a valid size
            int size = bottom - top;
    
            // set the size for the drawable, maybe scale up / down, maybe center...
            mDrawable.setBounds(left, top, left + size, top + size);
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
    
            // get the width of this view and calculate the width used per dot
            float widthPerDot = getWidth() / (float) NUM_DOTS;
    
            // draw every single dot where it belongs.
            for (int i = 0; i < NUM_DOTS; i++) {
                // just redraw the same drawable and move it along the x axis
                mDrawable.getBounds().offsetTo((int) ((i * widthPerDot)), 0);
    
                // finally just draw the dot
                mDrawable.draw(canvas);
            }
        }
        public class DotDrawable extends android.graphics.drawable.Drawable {
    
            Paint mPaint;
    
            DotDrawable() {
                mPaint = new Paint();
                mPaint.setColor(Color.RED);
                mPaint.setStyle(Paint.Style.FILL);
            }
    
            @Override
            public void draw(Canvas canvas) {
                Rect bounds = getBounds();
                canvas.drawCircle(bounds.centerX(), bounds.centerY(), Math.min(bounds.width(), bounds.height()) / 2, mPaint);
            }
    
            @Override
            public void setAlpha(int alpha) {
    
            }
    
            @Override
            public void setColorFilter(ColorFilter cf) {
    
            }
    
            @Override
            public int getOpacity() {
                return 0;
            }
        }
    }
    
    public类DotsView扩展视图{
    专用静态最终int NUM_DOTS=25;
    私人可提取;
    公共点视图(上下文){
    超级(上下文);
    init();
    }
    公共点视图(上下文、属性集属性){
    超级(上下文,attrs);
    init();
    }
    公共点视图(上下文、属性集属性、int defStyleAttr){
    super(上下文、attrs、defStyleAttr);
    init();
    }
    私有void init(){
    mDrawable=new DotDrawable();
    }
    @凌驾
    测量时的保护空隙(内部宽度测量等级、内部高度测量等级){
    //在你应该测量的地方测量:在onMeasure中
    //获得所有宽度
    //这将适用于match_父项,以及宽度为32dp的情况
    int availableWidth=MeasureSpec.getSize(widthmasurespec);
    //NUM_DOTS==25或其他任何值,使用常量或读入属性
    int neededHeight=(int)(可用宽度/(浮动)点数);
    //将测量尺寸标注应用于视图
    设置测量尺寸(可用宽度、所需高度);
    }
    @凌驾
    仅限受保护的空心布局(布尔值已更改、整数左侧、整数顶部、整数右侧、整数底部){
    超级。仅限布局(已更改、左、上、右、下);
    //测量后调用。这里我们知道视图的实际大小
    //假设它是正方形,否则请执行Math.min以获得有效大小
    int size=底部-顶部;
    //设置可拉伸的尺寸,可能放大/缩小,可能居中。。。
    mDrawable.setBounds(左、上、左+大小、上+大小);
    }
    @凌驾
    受保护的void onDraw(画布){
    super.onDraw(帆布);
    //获取此视图的宽度并计算每个点使用的宽度
    float widthPerDot=getWidth()/(float)NUM_DOTS;
    //把每个点都画在它所属的地方。
    对于(int i=0;isetMeasuredDimension(availableWidth, neededHeight);
    
    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
    
        // called after measuring. here we know the actual size of our view
    
        // assuming it's a square, else do some Math.min to get a valid size
        int size = bottom - top;
    
        // set the size for the drawable, maybe scale up / down, maybe center...
        mDrawable.setBounds(left, top, left + size, top + size);
    }
    
    public class DotsView extends View {
        private static final int NUM_DOTS = 25;
    
        private Drawable mDrawable;
    
        public DotsView(Context context) {
            super(context);
            init();
        }
    
        public DotsView(Context context, AttributeSet attrs) {
            super(context, attrs);
            init();
        }
    
        public DotsView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            init();
        }
    
        private void init() {
            mDrawable = new DotDrawable();
        }
    
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            // measure where you're supposed to measure: in onMeasure
    
            // get all of the width
            // this will work with match_parent, as well as with a width of 32dp
            int availableWidth = MeasureSpec.getSize(widthMeasureSpec);
    
            // NUM_DOTS == 25, or whatever, use a constant or read into attributes
            int neededHeight = (int) (availableWidth / (float) NUM_DOTS);
    
            // apply the measure dimension to the view
            setMeasuredDimension(availableWidth, neededHeight);
        }
    
        @Override
        protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
            super.onLayout(changed, left, top, right, bottom);
    
            // called after measuring. here we know the actual size of our view
    
            // assuming it's a square, else do some Math.min to get a valid size
            int size = bottom - top;
    
            // set the size for the drawable, maybe scale up / down, maybe center...
            mDrawable.setBounds(left, top, left + size, top + size);
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
    
            // get the width of this view and calculate the width used per dot
            float widthPerDot = getWidth() / (float) NUM_DOTS;
    
            // draw every single dot where it belongs.
            for (int i = 0; i < NUM_DOTS; i++) {
                // just redraw the same drawable and move it along the x axis
                mDrawable.getBounds().offsetTo((int) ((i * widthPerDot)), 0);
    
                // finally just draw the dot
                mDrawable.draw(canvas);
            }
        }
        public class DotDrawable extends android.graphics.drawable.Drawable {
    
            Paint mPaint;
    
            DotDrawable() {
                mPaint = new Paint();
                mPaint.setColor(Color.RED);
                mPaint.setStyle(Paint.Style.FILL);
            }
    
            @Override
            public void draw(Canvas canvas) {
                Rect bounds = getBounds();
                canvas.drawCircle(bounds.centerX(), bounds.centerY(), Math.min(bounds.width(), bounds.height()) / 2, mPaint);
            }
    
            @Override
            public void setAlpha(int alpha) {
    
            }
    
            @Override
            public void setColorFilter(ColorFilter cf) {
    
            }
    
            @Override
            public int getOpacity() {
                return 0;
            }
        }
    }