andorid自定义组件,涂鸦的实现

      最近看了很多关于自定义控件的博客,其中有个关于涂鸦的博文中的例子在我的手机上就无法合适的使用,例如,我手指移动在屏幕右侧,结果线却画在了中间的位置,还以为是我手机的问题,结果发现只是代码中对各种大小和布局都是写死的。
  非常难说像android自带控件一般,随便设大小,边距这些,功能也不会变样,所以在此要做一个比较正常的涂鸦控件,主要是能够像普通android控件一般,能够在xml中设置样式直接控制该控件外观。
      首先该View从onMeasure()方法中获得自己的大小,接着再根据这个大小初始化Bitmap cacheBitmap,这个bitmap用来保存画的笔画的,同时也是该view的draw方法中需要画出来,展现给用户看的效果的bitmap
      然后,就是用OnTouch()方法来获得用户滑动的位置和状态来不断在cacheBitmap的画出路径

      最后,调用invalidate(),通知View调用自己的draw()方法


直接上代码吧,里面也有比较详细的注释了

<span style="font-size:18px;">public class TuyaView extends View {

	private Canvas canvas = null;
	private Paint paint = null;
	private Bitmap cacheBitmap = null;
	private Path path = null;
	// 记录路径path的起始位置
	float x, y;

	public TuyaView(Context context) {
		super(context);
		// TODO Auto-generated constructor stub
	}

	public TuyaView(Context context, AttributeSet attrs) {
		super(context, attrs);
		// TODO Auto-generated constructor stub
	}

	public TuyaView(Context context, AttributeSet attrs, int defStyleAttr) {
		super(context, attrs, defStyleAttr);
		// TODO Auto-generated constructor stub
	}
	
	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		// TODO Auto-generated method stub
		// widthMeasureSpec、heightMeasureSpec是有高16位模式和低16位size,所以看起来会很大
		// 获得模式
		int mode = MeasureSpec.getMode(widthMeasureSpec);
		// AT_MOST限制最大尺寸,EXACTLY则是确定好尺寸了,例如在xml文件中设置了大小
		if (mode == MeasureSpec.AT_MOST || mode == MeasureSpec.EXACTLY) {
			// 获得View实际大小
			int width = MeasureSpec.getSize(widthMeasureSpec);
			int height = MeasureSpec.getSize(heightMeasureSpec);
			init(width , height);
			// 以下方法是设置View大小的方法,除非想在代码中控制大小,不然不应该在这里填写大小
			// 如果希望其生效则不要调用super.onMeasure(widthMeasureSpec, heightMeasureSpec);这句了
			// setMeasuredDimension(400, height);
		} else if (mode == MeasureSpec.UNSPECIFIED) { // View的大小不确定时
			Log.d("WOGU", "mode=UNSPECIFIED");
		}
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
	}

	private void init(int width , int height) {
		cacheBitmap = Bitmap.createBitmap(width, height, Config.ARGB_8888);
		paint = new Paint(Paint.DITHER_FLAG);
		path = new Path();
		
		paint.setColor(Color.RED);
		paint.setStyle(Paint.Style.FILL_AND_STROKE);
		//设置画笔笔画宽度
		paint.setStrokeWidth(1);
		paint.setAntiAlias(true);
		paint.setDither(true);
		canvas = new Canvas();
		canvas.setBitmap(cacheBitmap);
		
	
	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		// TODO Auto-generated method stub
		float curX = event.getX();
		float curY = event.getY();
		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			x = curX;
			y = curY;
			path.moveTo(x, y);
			break;
		case MotionEvent.ACTION_UP:
			path.quadTo(x, y, curX, curY);
			canvas.drawPath(path, paint);
			path.reset();
			break;
		case MotionEvent.ACTION_MOVE:
			path.quadTo(x, y, curX, curY);
			canvas.drawPath(path, paint);
			x = curX;
			y = curY;
			path.moveTo(x, y);
			break;
		}
		
		invalidate();
		return true;
	}

	@Override
	protected void onDraw(Canvas canvas) {
		// TODO Auto-generated method stub
		canvas.drawBitmap(cacheBitmap, 0, 0 ,paint);
		super.onDraw(canvas);
	}

}</span>
直接在xml文件中控制大小和背景等,不影响该自定义view的使用
<com.example.tuyaview.TuyaView 
        android:layout_width="fill_parent"
        android:layout_height="300dp"
        android:background="@color/default_bg"
        android:layout_gravity="center_horizontal|center"
        />

完整应用在  这里,不过基本没什么其他内容了


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的自定义View实现K线图的代码示例: ``` public class KLineView extends View { private List<KLineData> mData; // K线数据 private int mWidth, mHeight; // 控件宽度、高度 private int mTopPadding; // 顶部留白 private int mBottomPadding; // 底部留白 private Paint mLinePaint; // K线画笔 private Paint mTextPaint; // 文本画笔 private float mMaxPrice, mMinPrice; // 最高价、最低价 private float mPriceScale; // 价格缩放比例 private float mVolumeScale; // 成交量缩放比例 private int mCandleWidth; // K线宽度 private int mCandleSpace; // K线间隔 public KLineView(Context context) { this(context, null); } public KLineView(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } public KLineView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); // 初始化画笔 mLinePaint = new Paint(); mLinePaint.setStrokeWidth(2f); mLinePaint.setAntiAlias(true); mLinePaint.setStyle(Paint.Style.STROKE); mTextPaint = new Paint(); mTextPaint.setTextSize(24f); mTextPaint.setAntiAlias(true); mTextPaint.setColor(Color.BLACK); // 初始化K线宽度、间隔 mCandleWidth = dp2px(8); mCandleSpace = dp2px(4); // 初始化留白 mTopPadding = dp2px(20); mBottomPadding = dp2px(20); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); // 获取控件宽度、高度 mWidth = MeasureSpec.getSize(widthMeasureSpec); mHeight = MeasureSpec.getSize(heightMeasureSpec); // 计算价格、成交量缩放比例 mPriceScale = (mHeight - mTopPadding - mBottomPadding) / (mMaxPrice - mMinPrice); mVolumeScale = (mHeight - mTopPadding - mBottomPadding) / getMaxVolume(); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // 绘制K线图 drawCandles(canvas); // 绘制边框 drawBorder(canvas); // 绘制Y轴标签 drawYLabels(canvas); } /** * 绘制K线图 */ private void drawCandles(Canvas canvas) { if (mData == null || mData.size() == 0) { return; } mLinePaint.setColor(Color.RED); mLinePaint.setStyle(Paint.Style.FILL); float startX = mCandleWidth / 2 + mCandleSpace; // 第一个K线起始X坐标 float startY = 0f; // K线起始Y坐标 float endX, endY; // K线结束X坐标、Y坐标 for (int i = 0; i < mData.size(); i++) { KLineData data = mData.get(i); // 计算K线顶部、底部Y坐标 float highY = mTopPadding + (mMaxPrice - data.getHigh()) * mPriceScale; float lowY = mTopPadding + (mMaxPrice - data.getLow()) * mPriceScale; // 计算K线开盘、收盘Y坐标 float openY = mTopPadding + (mMaxPrice - data.getOpen()) * mPriceScale; float closeY = mTopPadding + (mMaxPrice - data.getClose()) * mPriceScale; // 绘制K线实体 if (data.getOpen() < data.getClose()) { mLinePaint.setColor(Color.RED); } else { mLinePaint.setColor(Color.GREEN); } canvas.drawRect(startX - mCandleWidth / 2, closeY, startX + mCandleWidth / 2, openY, mLinePaint); // 绘制K线上下影线 mLinePaint.setColor(Color.BLACK); canvas.drawLine(startX, highY, startX, openY, mLinePaint); canvas.drawLine(startX, lowY, startX, closeY, mLinePaint); // 更新起始X坐标 startX += mCandleWidth + mCandleSpace; } } /** * 绘制边框 */ private void drawBorder(Canvas canvas) { mLinePaint.setColor(Color.BLACK); mLinePaint.setStyle(Paint.Style.STROKE); canvas.drawRect(0, 0, mWidth, mHeight, mLinePaint); } /** * 绘制Y轴标签 */ private void drawYLabels(Canvas canvas) { float labelHeight = mTextPaint.descent() - mTextPaint.ascent(); // 绘制价格标签 float priceLabelY = mTopPadding - mTextPaint.ascent(); for (float price = mMinPrice; price <= mMaxPrice; price += (mMaxPrice - mMinPrice) / 5) { float priceY = mTopPadding + (mMaxPrice - price) * mPriceScale; canvas.drawText(String.format(Locale.getDefault(), "%.2f", price), 0, priceY + labelHeight / 2, mTextPaint); canvas.drawLine(mCandleWidth / 2, priceY, mWidth, priceY, mLinePaint); } // 绘制成交量标签 float volumeLabelY = mHeight - mBottomPadding - mTextPaint.descent(); float maxVolume = getMaxVolume(); for (int i = 0; i < 5; i++) { float volumeY = volumeLabelY - i * labelHeight * maxVolume / 5; canvas.drawText(String.format(Locale.getDefault(), "%.0f", maxVolume * i / 5), 0, volumeY - labelHeight / 2, mTextPaint); } } /** * 设置K线数据 */ public void setData(List<KLineData> data) { mData = data; calculateMaxAndMinPrice(); requestLayout(); invalidate(); } /** * 计算最高价、最低价 */ private void calculateMaxAndMinPrice() { if (mData == null || mData.size() == 0) { return; } mMaxPrice = mData.get(0).getHigh(); mMinPrice = mData.get(0).getLow(); for (int i = 1; i < mData.size(); i++) { KLineData data = mData.get(i); mMaxPrice = Math.max(mMaxPrice, data.getHigh()); mMinPrice = Math.min(mMinPrice, data.getLow()); } } /** * 获取最大成交量 */ private float getMaxVolume() { float maxVolume = 0f; if (mData != null && mData.size() > 0) { for (KLineData data : mData) { maxVolume = Math.max(maxVolume, data.getVolume()); } } return maxVolume; } /** * dp转px */ private int dp2px(int dp) { return (int) (dp * getContext().getResources().getDisplayMetrics().density + 0.5f); } } ``` 其中,KLineData表示K线数据,包括开盘价、收盘价、最高价、最低价、成交量等属性。可以根据实际需求进行修改。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值