计算位图图块高度,以便不裁剪最后一个项目

时间:2016-11-07 14:45:26

标签: android android-layout bitmap android-custom-view

问题是要创建平铺背景,它将以X轴拉伸,使得最后一个元素不会被裁剪。

我想要什么(计算每个"点"图像的高度,以便最后一块瓷砖不会被裁剪): enter image description here

我拥有的内容(请参阅裁剪的最后一个元素):

enter image description here

应该有一种方法可以创建自定义类扩展ImageViewBitmap并手动实现该计算。但我无法获得有关如何正确执行此操作的任何信息。

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"/>

以及它的使用方式:

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@drawable/bg_dots" />

编辑:

感谢@ paulina_glab的文章和信息,我已经能够创建自定义视图,绘制重复的tile drawable。但是仍然存在计算位图的实际宽度\高度以适应屏幕高度的问题。

计算我确实涉及浮动到整数轮并且带来delta。位图需要整数作为宽度和高度,但计算显示它应该是浮点值以准确适合屏幕。

这是我带来的:

public class DotsView extends View {
private Rect rect;
private Bitmap mBitmap;
private BitmapDrawable mDrawable;

private int screenWidth;
private int screenHeight;
private int calculatedHeight;
private int calculatedWidth;

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(){
    Resources res = getResources();

    //get window specs
    WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
    Display display = wm.getDefaultDisplay();
    Point size = new Point();
    display.getSize(size);


    //get bitmap from resource image
    mBitmap =  BitmapFactory.decodeResource(res, R.drawable.ic_dot);

    //define width and height of custom view after all modifications
    screenWidth = size.x;
    screenHeight = size.y;

    //calculate proportion of height\width so the last element wont be cropped
    calculatedHeight = calculateHeight();
    calculatedWidth = calculateWidth();
    mBitmap = Bitmap.createScaledBitmap(mBitmap, calculatedWidth, calculatedHeight, false);

    //create drawable from customized bitmap
    mDrawable = new BitmapDrawable(res, mBitmap);
    //set it to be repeating tile
    mDrawable.setTileModeXY(Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);

    //define borders of drawable to de drawn in
    rect = new Rect(0, 0, screenWidth, calculatedHeight);
    mDrawable.setBounds(rect);
}

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    mDrawable.draw(canvas);
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(screenWidth, calculatedHeight);
    setMeasuredDimension(screenWidth, calculatedHeight);
}

private int calculateWidth(){
    /*such calculation brings delta from conversion float to int. This delta doesn't let to draw
    * exactly 25 dots. Any workarounds? */
    return (int)(mBitmap.getWidth() / ((float)(mBitmap.getWidth() * 25) / screenWidth)); //draw around 25 dots
}

private int calculateHeight(){
    return (int)(screenHeight * 0.07); //make it around 7 percents of screen
}
}

请查看calculateWidth()方法。

它看起来像什么(由于三角洲仍有裁剪点): enter image description here

2 个答案:

答案 0 :(得分:1)

我不知道通过bitmap / xml做出你想做的事情的方法,但是(如你所想)我可以建议创建自定义View课程。

View.onDraw()方法允许您定义自定义绘图过程并在

View.onSizeChanged()你可以计算正确的点数/高度

您可以使用Drawable.draw()绘制 单点

比你得到一个自定义wiget ,你可以将它放在一些布局容器中(例如TextView,而不是android:background)。< / p>

我写了一个article about drawing custom views - 描述的案例比较复杂,但你可以看到我上面提到的每种方法的用法。例如,您可以跳过有关Path或属性的段落。

这是一个official tutorial。这可能对你有用,但它非常笼统。

答案 1 :(得分:1)

如果你想在Android系统中进行自定义绘图,你应该只使用Android框架,测量和布局等内容会更容易。

return (int)(screenHeight * 0.07); //make it around 7 percents of screen

如果您“猜测”onMeasure中的高度,则表示您并未真正测量视图。你想要25个点,沿着视图的宽度分布,所以这就是你应该做的。

  1. 摆脱WindowManagerDisplay等Android。现在Android有分屏模式,软导航栏......屏幕尺寸并不重要。
  2. 使用Android框架。
  3. 它始终是相同的过程:测量,布局,绘制。

    测量

    您在onMeasure,所以衡量widthMeasureSpec包含您需要的信息 - 在这种情况下是可用的宽度。请使用MeasureSpec.getSize(widthMeasureSpec)阅读。

    现在你想要沿着这个宽度分25个点并且分别有一个高度...所以你需要做的就是划分width / 25,你就会知道你的视图应该有多高。

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

    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);
    }
    

    图纸

    您可以通过在画布上创建一些Paint并执行一些drawCircle(...)来完成自定义绘图,或者只使用一些Drawable。如何绘制圆点有无穷无尽的可能性。

    你想要它25次,所以添加一个循环并确保沿x轴移动它。现在你已经开始了!

    由于这需要掌握很多,这里有一个简单的例子,带有一个基本的drawable。我还写了一篇关于view basics的文章,其中包含有关测量和布局的更多信息。试着看看它是如何工作的(我自己喜欢在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;
            }
        }
    }