问题是要创建平铺背景,它将以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"/>
以及它的使用方式:
<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()
方法。
答案 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个点,沿着视图的宽度分布,所以这就是你应该做的。
WindowManager
,Display
等Android。现在Android有分屏模式,软导航栏......屏幕尺寸并不重要。它始终是相同的过程:测量,布局,绘制。
您在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;
}
}
}