如何创建自定义视图

Posted

技术标签:

【中文标题】如何创建自定义视图【英文标题】:How to create Custom Views 【发布时间】:2014-03-13 00:42:20 【问题描述】:

您好,如何为我的应用创建自定义视图?观察了谷歌的例子,但不能正确......如果有人知道关于这个主题的任何教程,我感谢!晚安!

【问题讨论】:

【参考方案1】:

创建 View 的子类。实现三个 View 构造函数。实现 onSizeChanged()onDraw()。可选择实现 onMeasure()onLayout()onTouchEvent() 等。

确实,您最好的方法是从骨架模板开始并对其进行扩展。这就是我一直做的:

/**
 * $Id: custom.hlp,v 1.15 2013-10-24 23:20:01 falk Exp $
 *
 * Template.java - Template widget
 *
 * Author: Edward A. Falk
 *         efalk@users.sourceforge.net
 *
 * Date: May 2009
 *
 * This code demonstrates the creation of a custom widget,
 * with some custom resources.
 *
 * Copy and paste this to your own class.
 * Delete the parts you don't need.
 */

package com.example.template;

import android.view.View;
import android.content.Context;
import android.content.res.Resources;
import android.util.AttributeSet;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;

import android.util.Log;

public class Template extends View 

    private static final String TAG = "Template";
    private Context ctx;
    private Resources res;
    private Paint paint;

    // Some XML resources:
    private int function;
    private String label;

    /**
     * Constructor used when view is constructed in code.
     */
    public Template(Context ctx) 
        super(ctx);
        TemplateInit(ctx, null);
    

    /**
     * Constructor used when view is constructed from XML
     */
    public Template(Context ctx, AttributeSet attrs) 
        super(ctx, attrs);
        TemplateInit(ctx, attrs);
    

    /**
     * Same, with class-specific base style
     */
    public Template(Context ctx, AttributeSet attrs, int defStyle) 
        super(ctx, attrs, defStyle);
        TemplateInit(ctx, attrs);
    

    private void TemplateInit(Context ctx, AttributeSet attrs)
    
        // Handle whatever common initialization is appropriate for
        // this widget.
        this.ctx = ctx;
        res = getResources();
        paint = new Paint();
        paint.setAntiAlias(true);
        paint.setDither(false);
    

    @Override
    protected void onSizeChanged(int w, int h, int ow, int oh)
    
        // Deal with size change
    

    @Override
    protected void onDraw(Canvas canvas)
    
        super.onDraw(canvas);

        paint.setColor(Color.BLACK);
        canvas.drawText(label, x, y, paint);
    

    /**
     * Receive touch events. Leave this out if the default
     * behavior is ok.
     */
    @Override
    public final boolean onTouchEvent(MotionEvent event) 
        // Return false to let some other view have it
        // Return true to say we handled it
        // Or let the superclass deal with it
        return super.onTouchEvent(event);
    

    /**
     * Determine how large we want to be.  We can skip implementing
     * this method if the superclass will handle it for us.
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
    
        int wid = View.MeasureSpec.getSize(widthMeasureSpec);
        int hgt = View.MeasureSpec.getSize(heightMeasureSpec);
        int wmode = View.MeasureSpec.getMode(widthMeasureSpec);
        int hmode = View.MeasureSpec.getMode(heightMeasureSpec);
        final int hpad = getPaddingLeft() + getPaddingRight();
        final int vpad = getPaddingTop() + getPaddingBottom();

        // Overview:  Measure our contents plus internal padding.
        // Pass this information to setMeasuredDimensions().  Whatever
        // values we pass, we can assume that's what our final size
        // will be.  If the parent doesn't like it, it will call this
        // method again, with more restrictions.

        // What happens now depends on the measure spec mode.  If
        // it's EXACTLY, we ignore our own needs and return the
        // size part of the measurespec.  If the mode is UNSPECIFIED,
        // we ignore the size part of the measurespec and return
        // anything we want.  If the mode is AT_MOST, we might return
        // the lesser of our own needs and the size part of
        // measurespec, or we might just treat this the same
        // as EXACTLY.

        // Easiest implementation is to use setMinimumWidth()/Height()
        // to establish our minimum required size, and then let
        // getSuggestedMinimumWidth()/Height() and getDefaultSize()
        // do the heavy lifting.  These may be overridden if needed,
        // to customize behavior.

        setMinimumWidth(hpad + yaddayadda);
        setMinimumHeight(vpad + yaddayadda);
        setMeasuredDimension(
          getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
          getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
    

    /**
     * Parent has made final decision and is laying us out.  Here
     * is where we do any internal layout we need.
     */
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b)
    
    

您甚至可以定义和实现自定义属性,但这很复杂,我建议不要这样做。

【讨论】:

以上是关于如何创建自定义视图的主要内容,如果未能解决你的问题,请参考以下文章

Android:如何根据大小创建自定义布局

如何创建 IBDesignable 自定义视图?

如何使用 Kotlin 创建自定义视图的构造函数

创建自定义视图的步骤

ios) 如何在自定义键盘上方创建建议视图

如何使用 xib 创建自定义视图