Custom Drawing

本文深入探讨了自定义视图中至关重要的onDraw()方法的使用,包括如何创建绘图对象、处理布局事件以及实现高效的绘制流程。通过实例分析,详细解释了如何在自定义视图中正确绘制形状、文本以及复杂图形,同时优化布局以适应不同屏幕尺寸和密度,提升用户体验。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Override onDraw()


The most important step in drawing a custom view is to override the onDraw() method. The parameter to onDraw() is a Canvasobject that the view can use to draw itself. The Canvas class defines methods for drawing text, lines, bitmaps, and many other graphics primitives. You can use these methods in onDraw() to create your custom user interface (UI).

Before you can call any drawing methods, though, it's necessary to create a Paint object. The next section discusses Paint in more detail.

Create Drawing Objects


The android.graphics framework divides drawing into two areas:

  • What to draw, handled by Canvas
  • How to draw, handled by Paint.

For instance, Canvas provides a method to draw a line, while Paint provides methods to define that line's color.Canvas has a method to draw a rectangle, while Paint defines whether to fill that rectangle with a color or leave it empty. Simply put, Canvas defines shapes that you can draw on the screen, while Paint defines the color, style, font, and so forth of each shape you draw.

So, before you draw anything, you need to create one or more Paint objects. The PieChart example does this in a method called init, which is called from the constructor:

private void init() {
   mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
   mTextPaint.setColor(mTextColor);
   if (mTextHeight == 0) {
       mTextHeight = mTextPaint.getTextSize();
   } else {
       mTextPaint.setTextSize(mTextHeight);
   }

   mPiePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
   mPiePaint.setStyle(Paint.Style.FILL);
   mPiePaint.setTextSize(mTextHeight);

   mShadowPaint = new Paint(0);
   mShadowPaint.setColor(0xff101010);
   mShadowPaint.setMaskFilter(new BlurMaskFilter(8, BlurMaskFilter.Blur.NORMAL));

   ...

Creating objects ahead of time is an important optimization. Views are redrawn very frequently, and many drawing objects require expensive initialization. Creating drawing objects within your onDraw() method significantly reduces performance and can make your UI appear sluggish.

Handle Layout Events


In order to properly draw your custom view, you need to know what size it is. Complex custom views often need to perform multiple layout calculations depending on the size and shape of their area on screen. You should never make assumptions about the size of your view on the screen. Even if only one app uses your view, that app needs to handle different screen sizes, multiple screen densities, and various aspect ratios in both portrait and landscape mode.

Although View has many methods for handling measurement, most of them do not need to be overridden. If your view doesn't need special control over its size, you only need to override one method: onSizeChanged().

onSizeChanged() is called when your view is first assigned a size, and again if the size of your view changes for any reason. Calculate positions, dimensions, and any other values related to your view's size in onSizeChanged(), instead of recalculating them every time you draw. In the PieChart example, onSizeChanged() is where thePieChart view calculates the bounding rectangle of the pie chart and the relative position of the text label and other visual elements.

When your view is assigned a size, the layout manager assumes that the size includes all of the view's padding. You must handle the padding values when you calculate your view's size. Here's a snippet fromPieChart.onSizeChanged() that shows how to do this:

       // Account for padding
       float xpad = (float)(getPaddingLeft() + getPaddingRight());
       float ypad = (float)(getPaddingTop() + getPaddingBottom());

       // Account for the label
       if (mShowText) xpad += mTextWidth;

       float ww = (float)w - xpad;
       float hh = (float)h - ypad;

       // Figure out how big we can make the pie.
       float diameter = Math.min(ww, hh);

If you need finer control over your view's layout parameters, implement onMeasure(). This method's parameters are View.MeasureSpec values that tell you how big your view's parent wants your view to be, and whether that size is a hard maximum or just a suggestion. As an optimization, these values are stored as packed integers, and you use the static methods of View.MeasureSpec to unpack the information stored in each integer.

Here's an example implementation of onMeasure(). In this implementation, PieChart attempts to make its area big enough to make the pie as big as its label:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
   // Try for a width based on our minimum
   int minw = getPaddingLeft() + getPaddingRight() + getSuggestedMinimumWidth();
   int w = resolveSizeAndState(minw, widthMeasureSpec, 1);

   // Whatever the width ends up being, ask for a height that would let the pie
   // get as big as it can
   int minh = MeasureSpec.getSize(w) - (int)mTextWidth + getPaddingBottom() + getPaddingTop();
   int h = resolveSizeAndState(MeasureSpec.getSize(w) - (int)mTextWidth, heightMeasureSpec, 0);

   setMeasuredDimension(w, h);
}

There are three important things to note in this code:

  • The calculations take into account the view's padding. As mentioned earlier, this is the view's responsibility.
  • The helper method resolveSizeAndState() is used to create the final width and height values. This helper returns an appropriate View.MeasureSpec value by comparing the view's desired size to the spec passed intoonMeasure().
  • onMeasure() has no return value. Instead, the method communicates its results by callingsetMeasuredDimension(). Calling this method is mandatory. If you omit this call, the View class throws a runtime exception.

Draw!


Once you have your object creation and measuring code defined, you can implement onDraw(). Every view implements onDraw() differently, but there are some common operations that most views share:

For example, here's the code that draws PieChart. It uses a mix of text, lines, and shapes.

protected void onDraw(Canvas canvas) {
   super.onDraw(canvas);

   // Draw the shadow
   canvas.drawOval(
           mShadowBounds,
           mShadowPaint
   );

   // Draw the label text
   canvas.drawText(mData.get(mCurrentItem).mLabel, mTextX, mTextY, mTextPaint);

   // Draw the pie slices
   for (int i = 0; i < mData.size(); ++i) {
       Item it = mData.get(i);
       mPiePaint.setShader(it.mShader);
       canvas.drawArc(mBounds,
               360 - it.mEndAngle,
               it.mEndAngle - it.mStartAngle,
               true, mPiePaint);
   }

   // Draw the pointer
   canvas.drawLine(mTextX, mPointerY, mPointerX, mPointerY, mTextPaint);
   canvas.drawCircle(mPointerX, mPointerY, mPointerSize, mTextPaint);
}
Sure! Here's an example program that meets your requirements: ```java import java.awt.*; public class NameDrawing extends Frame { public NameDrawing() { super("Graphical Representation of My Name"); setSize(500, 300); setVisible(true); addWindowListener(new java.awt.event.WindowAdapter() { public void windowClosing(java.awt.event.WindowEvent we) { dispose(); } }); } public void paint(Graphics g) { // Draw colored rectangles g.setColor(Color.red); g.fillRect(50, 50, 100, 100); g.setColor(Color.green); g.fillRect(200, 50, 100, 100); g.setColor(Color.blue); g.fillRect(350, 50, 100, 100); // Draw hanzi for 程 g.setColor(Color.white); g.drawLine(75, 75, 125, 75); g.drawLine(75, 75, 75, 125); g.drawLine(75, 125, 125, 125); g.drawLine(125, 75, 125, 125); g.drawLine(75, 100, 125, 100); // Draw hanzi for 嘉 g.drawLine(225, 75, 275, 75); g.drawLine(225, 75, 225, 125); g.drawLine(225, 125, 275, 125); g.drawLine(275, 75, 275, 125); g.drawLine(225, 100, 275, 100); g.drawLine(250, 75, 250, 125); // Draw hanzi for 一 g.drawLine(375, 75, 425, 75); g.drawLine(375, 125, 425, 125); g.drawLine(400, 75, 400, 125); } public static void main(String[] args) { new NameDrawing(); } } ``` This program creates a window using AWT, with colored rectangles and hanzi for each character of my name drawn using simple lines. The `paint()` method is overridden to handle the custom drawing. The rectangles are filled with different colors, and each hanzi is drawn using only lines. You could further improve this program by organizing the drawing code into separate methods or classes, using variables to define the rectangle sizes and positions, and adding more characters to the name if desired.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值