自定义View构造函数参数理解

View自定义的属性时的流程

  1. 通过<declare-styleable>为自定义View添加属性
  2. 在运行时(一般为构造函数)获取属性值
  3. 获取到得属性值并应用到View

自定义View的属性和Style

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <declare-styleable name="Customize">
        <attr name="attr_one" format="string" />
        <attr name="attr_two" format="string" />
        <attr name="attr_three" format="string" />
        <attr name="attr_four" format="string" />
    </declare-styleable>

    <attr name="CustomizeStyle" format="reference" />

</resources>

上述的attrs.xml中我们声明了Customize与CustomizeStyle,其中Customize包含了attr_one、attr_two、attr_three与attr_four四个attribute,CustomizeStyle也是一个attribute,但是没有声明在declare-styleable中。
定义在declare-styleable中与直接用attr定义没有实质上的不同,上述attrs.xml中,无论attr_one~attr_four是否声明在declare-styleable中,系统都会为我们在R.attr中生成5个attribute。
attr class:

public static final class attr {
        public static final int CustomizeStyle = 2130771968;
        public static final int attr_four = 2130771972;
        public static final int attr_one = 2130771969;
        public static final int attr_three = 2130771971;
        public static final int attr_two = 2130771970;

        public attr() {
        }
    }

不同的是,如果在declare-styleable中,系统还会为我们在R.styleable中生成相关的属性。
styleable class:

public static final class styleable {
        public static final int[] Customize = new int[]{2130771969, 2130771970, 2130771971, 2130771972};
        public static final int Customize_attr_four = 3;
        public static final int Customize_attr_one = 0;
        public static final int Customize_attr_three = 2;
        public static final int Customize_attr_two = 1;

        public styleable() {
        }
    }

如上所示,R.styleable.Customize是一个int[],里面的元素的值正好与R.attr.attr_one~ R.attr.attr_four相对应,而R.styleabe.Customize_attr_one等4个值就是R.attr.attr_one~R.attr.attr_four在R.styleable.Customize数组中的索引。这个数组和索引在下面运行时获得索性值时会用到,将attr分组在declare-styleable中的作用就是系统会自动为我们生成这些东西,如果不声明在declare-styleabe中,我们完全可以在需要的时候自己构建这个数组,由于数组是自己构建的,每个属性的下标索引也就很清楚了,只是比较麻烦。

在xml中为相应地属性声明属性值

在xml中为属性赋值的几种不同方式

  1. 直接在layout中使用属性
    在xml layout中使用自定义属性与使用系统属性差不多,不过属性所属的namespace不同。
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:ad="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <com.angeldevil.customstyle.CustomTextView
        style="@style/ThroughStyle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/hello_world"
        ad:attr_one="attr one in xml" />

</RelativeLayout>

像layout_width等属性属于android的namespace,自定义的属性属于当前程序的namespace,只需要像声明android的namespace一样声明当前程序的namespace就好,只需要把android换成res-auto,应用属性的时候也需要注意属性的namespace。
2. 设置style并在style中设置属性
在上面xml中,我们为CustomTextView声明了一个style:ThroughStyle,这个Style很简单,只是指定了两个属性的值

<style name="ThroughStyle">
        <item name="attr_one">attr one from style</item>
        <item name="attr_two">attr two from style</item>
        <item name="android:text">text123</item>
    </style>

注意,在style中我们声明了attr_one的值,同时在xml中也直接像attr_one赋了值,最终用哪一个,有个优先级的问题,后面会在运行时获取属性值中在说。
3. theme中指定在当前Application或Activity中属性的默认值

<!-- Application theme. -->
    <style name="AppTheme" parent="AppBaseTheme">

        <!-- All customizations that are NOT specific to a particular API-level can go here. -->
        <item name="attr_one">attr one from theme</item>
        <item name="attr_two">attr two from theme</item>
        <item name="attr_three">attr three from theme</item>
        <item name="CustomizeStyle">@style/CustomizeStyleInTheme</item>
    </style>

    <style name="CustomizeStyleInTheme">
        <item name="attr_one">attr one from theme reference</item>
        <item name="attr_two">attr two from theme reference</item>
        <item name="attr_three">attr three from theme reference</item>
    </style>

在上述xml中,我们在AppTheme中为attr_one、attr_two与attr_three指定了值,但是同时也为CustomizeStyle指定了一个reference,而在这个reference中也为attr_one、attr_two与attr_three指定了值,CustomizeStyle就是我们之前在attrs.xml中定义的一个属性。在theme中为属性设置值的方式有两种,直接在theme中定义或通过另一个attribute引用一个style,这两种方式还是有区别的,我们可以在下面的获取属性值中可以看到。

在运行时获取属性值

在styles.xml中,我们同时定义一个DefaultCustomizeStyle的style,它的作用等下可以看到。

    <style name="DefaultCustomizeStyle">
        <item name="attr_one">attr one from defalut style res</item>
        <item name="attr_two">attr two from defalut style res</item>
        <item name="attr_three">attr three from defalut style res</item>
        <item name="attr_four">attr four from defalut style res</item>
    </style>

先看下CustomTextView的代码

public class CustomTextView extends TextView {

    private static final String TAG = CustomTextView.class.getSimpleName();

    public CustomTextView(Context context) {
        super(context);
    }

    public CustomTextView(Context context, AttributeSet attrs) {
        this(context, attrs, R.attr.CustomizeStyle);
    }

    public CustomTextView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);

        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Customize, defStyle,
                R.style.DefaultCustomizeStyle);
        String one = a.getString(R.styleable.Customize_attr_one);
        String two = a.getString(R.styleable.Customize_attr_two);
        String three = a.getString(R.styleable.Customize_attr_three);
        String four = a.getString(R.styleable.Customize_attr_four);
        Log.i(TAG, "one:" + one);
        Log.i(TAG, "two:" + two);
        Log.i(TAG, "three:" + three);
        Log.i(TAG, "four:" + four);
        a.recycle();
    }
}

主要的代码都在第三个函数中,这里也没做什么,只是通过Context的obtainStyledAttributes获得了前面定义的4个属性的值并打印了出来。

View的第三个构造函数的第三个参数defStyle

如果在Code中实例化一个View会调用第一个构造函数,如果在xml中定义调用第二个构造函数,而第三个函数系统是不调用的,要由View(我们自定义的或系统预定义的View,如此处的CustomTextView和Button)显示调用,比如在这里我们在第二个构造函数中调用了第三个构造函数,并将R.attr.CustomizeStyle传给了第三个参数。
第三个参数的意义就如同它的名字所说,是默认的Style,只是这里没有说清楚,这里的默认Style是指他在当前Application或Activity所用的Theme中的默认Style,以系统中的Button为例说明。

public Button(Context context) {
        this(context, null);
    }

    public Button(Context context, AttributeSet attrs) {
        this(context, attrs, com.android.internal.R.attr.buttonStyle);
    }

    public Button(Context context, AttributeSet attrs, int defStyleAttr) {
        this(context, attrs, defStyleAttr, 0);
    }

在Code中实例化View会调用第一个构造函数,在XML中定义会调用第二个构造函数,在Button的实现中都调用了第三个构造函数,并且defStyle的值是com.android.internal.R.attr.buttonStyle。buttonStyle是系统中定义的一个attribute,系统默认有一个Theme,比如4.0中是Theme.Holo。

下面是系统默认的Theme,为buttonStyle指定了一个Style

<style name="Widget.DeviceDefault.Button" parent="Widget.Holo.Button" >
</style>

<style name="Widget.Holo.Button" parent="Widget.Button">
<item name="android:background">@android:drawable/btn_default_holo_dark</item>
    <item name="android:textAppearance">?android:attr/textAppearanceMedium</item>
    <item name="android:textColor">@android:color/primary_text_holo_dark</item>
    <item name="android:minHeight">48dip</item>
    <item name="android:minWidth">64dip</item>
</style>

这个Style定义了系统中Button的默认属性,如background等。
从文档中第三个构造函数的说明中也可以看到,这个构造函数的作用是View的子类提供这个类的基础样式

View(Context context, AttributeSet attrs, int defStyleAttr)
Perform inflation from XML and apply a class-specific base style.

上面说的都比较抽象,还是直接看实例代码来的清楚明白,实验用的代码上面全都贴完了,这里直接看结果,但在这之前要先看一个函数:obtainStyledAtributes。
obtainStyledAtributes。

我们要获取的属性值都是通过这个函数返回的TypedArray获得的,这是官方说明:obtainStyledAttributes,以下是函数原型:

public TypedArray obtainStyledAttributes (AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes)

4个参数的意思分别是:

  • set:属性值的集合
  • attrs:我们要获取的属性的资源ID的一个数组,如同ContextProvider中请求数据库时的Projection数组,就是从一堆属性中我们希望查询什么属性的值
  • defStyleAttr:这个是当前Theme中的一个attribute,是指向style的一个引用,当在layout xml中和style中都没有为View指定属性时,会从Theme中这个attribute指向的Style中查找相应的属性值,这就是defStyle的意思,如果没有指定属性值,就用这个值,所以是默认值,但这个attribute要在Theme中指定,且是指向一个Style的引用,如果这个参数传入0表示不向Theme中搜索默认值
  • defStyleRes:这个也是指向一个Style的资源ID,但是仅在defStyleAttr为0defStyleAttr不为0但Theme中没有为defStyleAttr(在这个例子中:定义的reference:CustomizeStyle)属性赋值时起作用

链接中对这个函数说明勉强过得去,这里简要概括一下。对于一个属性可以在多个地方指定它的值,如XML中指定,style,Theme,而这些位置定义的值有一个优先级,按优先级从高到低依次是

直接在XML中定义 > xml中style定义 > 由defStyleAttr和defStyleRes指定的默认值 > 直接在Theme中指定的值(不是style,只是theme中定义的attr_one, attr_two…)

看这个关系式就比较明白了,defStyleAttr和defStyleRes在前面的参数说明中已经说了,“直接在Theme中指定的值”的意思在下面的示代码中可以看到。

在Code中是这样获取属性的

public CustomTextView(Context context, AttributeSet attrs) {
    this(context, attrs, R.attr.CustomizeStyle);
}

public CustomTextView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);

    TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Customize, defStyle,
                R.style.DefaultCustomizeStyle);
    ...
}

从代码中可以看到,R.attr.CustomizeStyle就是前面提到的defStyleAttr,R.style.DefaultCustomizeStyle就是defStyleRes。

在Styles.xml中我们为App定义了一个Theme:AppTheme,在这个Theme中直接为attr_one、attr_two、attr_three定义了属性值,这个就是前面关系式中说的直接在Theme中指定的值

我们回到顶部最初的代码片段,在AppTheme中同时定义了CustomizeStyle,引用了一个Style,在CustomTextView的构造函数中分别打印了attr_one、attr_two、attr_three、attr_four的值,所以下面我们就可以通过Log输出验证一下前面的关系式,如果一个attribute在多个位置都定义了值,究竟哪一个起作用。
如上图所示:

  • attr_one同时在xml、style、defStyleAttr、defStyleRes与Theme中直接定义了值,但起作用的是在xml中的定义
  • attr_two同时在style、defStyleAttr、defStyleRes与Theme中直接定义了值,但起用的是在style中的定义
  • attr_three同时在defStyleAttr、defStyleRes与Theme中直接定义了值,但起作用的是defStyleAttr
  • attr_four在defStyleRes中定了,但结果仍然是0。
    这可以说明:
  1. 直接在XML中定义 > style定义 > 由defStyleAttr定义的值 > defStyleRes指定的默认值、直接在Theme中指定的值
  2. defStyleAttr(即defStyle)不为0且在当前Theme中可以找到这个attribute的定义时,defStyleRes不起作用,所以attr_four虽然在defStyleRes(DefaultCustomizeStyle)中定义了,但取到的值仍为null
    为了看一下defStyleRes的作用,1. 我们在AppTheme中加上attr_four的定义,2. 向obtainStyledAttributes的第三个参数传入0,或者移除AppTheme中CustomizeStyle的定义,结果是一样的
<style name="AppTheme" parent="AppBaseTheme">
    <item name="attr_one">attr one from theme</item>
    <item name="attr_two">attr two from theme</item>
    <item name="attr_three">attr three from theme</item>
    <item name="attr_four">attr four from theme</item>
</style>

由于defStyleAttr为0,或者defStyleAttr不为0但是我们没有为这个属性赋值,所以defStyleRes起作用,当一个属性没有在XML和Style中赋值时,系统会在defStyleRes(此处为DefaultCustomizeStyle)查找属性的值。

我们看到attr_three的值来自defStyleRes,而attr_four的值来自Theme中的直接定义(DefaultCustomizeStyle定义了attr_one、atrr_two、attr_three) ,这就说明

  1. defStyleAttr即defStyle为0或Theme中没有定义defStyle时,defStyleRes才起作用
  2. defStyleRes > 在Theme中直接定义

结论

  1. 要为自定义View自定义属性,可以通过declare-styleable声明需要的属性
  2. 为了在Theme设置View的默认样式,可以同时定义一个format为reference的属性att_style,在定义Theme时为这个attribute指定一个Style,并且在View的第二个构造函数中将R.attr.attr_style 作为第三个参数调用第三个构造函数
  3. 可以定义一个Style作为Theme中没有定义attr_style时View属性的默认值。
  4. 可以在Theme中直接为属性赋值,但优先级最低
  5. 当defStyleAttr(即View的构造函数的第三个参数)不为0且在Theme中有为这个attr_style赋值时,defStyleRes(通过obtainStyledAttributes的第四个参数指定)不起作用
  6. 属性值定义的优先级:xml > xml style属性 > Theme中的默认Sytle > 默认Style(通过obtainStyledAttributes的第四个参数指定)> 在Theme中直接指定属性值
  • 7
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
闲着也是闲着的时候,打开探探划一划,挺多男同胞会这样吧。这不,我也是这样,看到首页探探的效果还是挺吸引人的。之前仿照实现了一个,效果还差一点,正好今天没事完善一下,写下来,希望看到能有收获。实现的效果首先看看实现后的效果,先不多说。当然跟探探的原版还是有差距的,没有在细节上面优化的更多。不过花时间调一调还是可以的,现在的效果可以看到,我在下面加了帧数的显示,在真机上显示还是很流畅的,模拟器上由于性能不行还是有点卡。实现的分析通过效果图可以看到,整体的实现可以分为以下四步:波纹涟漪的效果渐变扫描的效果和中间的镂空旋转点击头像的动画把以上步骤分别加以实现,就可以做到了。具体实现方法也不止一种,我这里选择的实现还算是简单易懂,易于实现的。以下分解各个步骤,并对关键的细节详加解释。如何实现因为有头像,并且涉及到加载网络图片。理论上来说我们可以直接继承ImageView来实现,可是这样太复杂了,是不可取的。所以头像跟我们现在所要实现效果是分开的。然后在跟头像组合在一起,这里可以使自定义一个ViewGroup把两者结合,我这里图省事,这里就没有去做了,而是直接在使用的时候,在布局里面组合在一起。所以第一步先不考虑头像而是实现TanTanRippleView.接下来看水波纹的实现: 我们需要的是,波纹是动态添加的,通过点击头像添加,所以需要暴露接口。并且波纹是有渐变的,越到边缘透明度越低,直到消失。每一个波纹都是一个圆,透明度通过改变Paint的颜色即可,透明度跟圆的半径也是有规律可循的。所以我这里把每个波纹做了封装。inner class RippleCircle {         // 4s * 60 frms = 240         private val slice = 150         var startRadius = 0f         var endRadius = 0f         var cx = 0f         var cy = 0f         private var progress = 0         fun draw(canvas: Canvas) {             if (progress >= slice) {                 // remove                 post {                     rippleCircles.remove(this)                 }                 return             }             progress             ripplePaint.alpha = (1 - progress.div(slice * 1.0f)).times(255).toInt()             val radis = startRadius   (endRadius - startRadius).div(slice).times(progress)             canvas.drawCircle(cx, cy, radis, ripplePaint)         }     }看到以上代码可能对slice这个属性有疑惑,这是定义波纹持续时间的,如果60帧每秒,那么持续4s,总共是240帧。这里默认取150帧,所以在60帧持续的时间是2.5s.透明度和半径都跟slice有关:ripplePaint.alpha = (1 - progress.div(slice * 1.0f)).times(255).toInt()             val radis = startRadius   (endRadius - startRadius).div(slice).times(progress)随着时间的增长,透明度越低,半径越大。怎么使用封装的RippleCircle。我们的要求是可以动态添加,并且消失之后需要移除,所以通过ArrayList来作为容器。但这里涉及到对集合的添加和删除操作,如果同时进行会发生异常。解决如下,使用CopyOnWriteArrayList,并且移除通过:post {                     rippleCircles.remove(this)                 }然后在onDraw中,值得一提的是为了防止被扫描的部分挡住,这里的代码需要写在onDraw方法的后部分。for (i in 0 until rippleCircles.size) {             rippleCircles[i].draw(canvas)         }在startRipple()方法中添加RippleCircle:rippleCircles.add(RippleCircle().apply {                 cx = width.div(2).toFloat()                 cy = height.div(2).toFloat()                 val maxRadius = Math.min(width, height).div(2).toFloat()                 startRadius = maxRadius.div(3)                 endRadius = maxRadius             })startRipple也是暴露出去调用添加波纹的方法。点击头像然后添加。涉及到自定义View当然测量是很关键的一部分。不过现在直接使用默认就可以,然后去宽高的最小值,除以2作为半径。在这里为什么startRadius要处以3呢,因为定义该大小作为波纹圆开始的半径。到这里第一步就算完成了。扫描的效果是关键的部分,而且效率直接影响是否可用。仔细看效果,其实也是一个圆只不过添加了shader。所以重点就是shader的实现。android中默认提供了几种Shader给我们使用。SweepGradient就是我们需要的,扫描渐变。然后选择了之后,就是调整参数了,看一下SweepGradient的用法: 构造函数SweepGradient(float cx, float cy,             @NonNull @ColorInt int colors[], @Nullable float positions[])重点在于positions 的理解。按照文档解释以及代码。 比如跟colors 的值一一对应,还必须是单调递增的,防止出现严重异常。 positions 对应每一个颜色的位置,当然是再圆的位置。顺时针,0为0°,0.5为180°,1为360°。 如果要像探探一样,最开始是一根线颜色很深。说明第一种颜色很深占比很小,第二种颜色浅占比很大,如下val colors = intArrayOf(getColor(R.color.pink_fa758a),getColor(R.color.pink_f5b8c2),getColor(R.color.top_background_color),getColor(R.color.white)) SweepGradient(width.div(2).toFloat(), height.div(2).toFloat(), colors, floatArrayOf(0f,0.001f,0.9f,1f))所以设置对了参数,整个扫描渐变的效果就差不多了。然后在对画笔设置shader,在drawCircle。backPaint.setShader(SweepGradient(width.div(2).toFloat(), height.div(2).toFloat(), colors, floatArrayOf(0f, 0.001f, 0.9f, 1f)))         canvas.drawCircle(width.div(2).toFloat(), height.div(2).toFloat(), radius, backPaint)当做完上面的操作之后,整个扫面的范围是整个圆,而需要的效果是中间有镂空的校园,这里又涉及到对xfermode的操作了。进行xfermode操作,必须要对canvas设置layer。如果不设置会有问题,镂空的校园是黑色的。详细的解释在我之间的文章中有高仿QQ 发送图片高亮HaloProgressView一文中做过阐述。setLayer需要设置范围,那么我们的范围就是覆盖整个大圆的矩形val rectF = RectF(width.div(2f) - radius                 , height.div(2f) - radius                 , width.div(2f)   radius                 , height.div(2f)   radius)         val sc = canvas.saveLayer(rectF, backPaint, Canvas.ALL_SAVE_FLAG)然后再drawCircle之后在设置xfermode        backPaint.setXfermode(PorterDuffXfermode(PorterDuff.Mode.DST_OUT))这里采取DST_OUT,为什么采用这种模式,在之前文章中可以详细查看Paint Xfermode 详解.到这里扫描渐变和镂空都实现了,只差最后一步,转动起来。 转动直接通过canvas的rotate方法是很适合现在的场景。因为整个View都是圆。涉及到canvas操作,需要save,然后再restorecanvas.save()         canvas.rotate(sweepProgress.toFloat(), width.div(2f), height.div(2f))         ...         canvas.restore()可以看到sweepProgress是转动的关键,通过动画控制是很方便的。private val renderAnimator by lazy {         ValueAnimator.ofInt(0, 60)                 .apply {                     interpolator = LinearInterpolator()                     duration = 1000                     repeatMode = ValueAnimator.RESTART                     repeatCount = ValueAnimator.INFINITE                     addUpdateListener {                         postInvalidateOnAnimation()                         fps                         sweepProgress                     }                     addListener(object : AnimatorListenerAdapter() {                         override fun onAnimationRepeat(animation: Animator?) {                             super.onAnimationRepeat(animation)                             fps = 0                         }                     })                 }     }可以看到参数设置一秒60次执行。也就是60帧。再通过到了360°,置0即可。到这里已经完成了TanTanRippleView的实现。接着实现头像的动画。在头像的点击事件里面直接添加:((TanTanRippleView)findViewById(R.id.ripple)).startRipple();                 AnimatorSet set = new AnimatorSet();                 set.setInterpolator(new BounceInterpolator());                 set.playTogether(                         ObjectAnimator.ofFloat(v,"scaleX",1.2f,0.8f,1f),                          ObjectAnimator.ofFloat(v,"scaleY",1.2f,0.8f,1f));                 set.setDuration(1100).start();
说明:本书稿为张孝祥、袁龙友两位老师在2000 年依据张孝祥的vc 讲课记录整理,由于时间关系,仅仅是写成了草稿,欢迎大家使用! 第1 章 掌握C 1.1 类的定义与应用 1.1.1 结构的定义 1.1.2 类的定义 1.1.2.1 类与结构 1.1.2.2 类的使用 (例子代码EX01-00) 1.2 函数的重载 1.2.1 重载参数个数不同的函数 (例子代码EX01-01) 1.2.2 重载参数数据类型不同的函数 (例子代码EX01-02) 1.3 构造函数与析构函数 1.3.1 构造函数 1.3.2 析构函数 (图1,没有)(图2,没有) 1.4 this 指针的引用 1.5 类的继承与protected 访问修饰符 1.5.1 单一继承 (例子代码EX01-03) (图x,没有) 1.5.2 多重继承 1.6 虚函数与多态性 1.7 类的书写规范 1.8 小结 第2 章 Windows 程序内部运行原理 2.1 Windows 应用程序,操作系统,计算机硬件之间的相互关系 2.1.1 关于API (图1,没有) 2.1.2 关于消息及消息队列 (图1、图2 没有) 2.2 什么是句柄 2.3 谈谈WinMain 函数 (例子代码EX02-00) 2.3.1 WinMain 函数的定义及功能 2.3.2 窗口及其生成 2.3.2.1 如何设计一个窗口类——步骤1 2.3.2.2 注册设计好的窗口类——步骤2 2.3.2.3 创建窗口——步骤3 2.3.2.4 显示创建的窗口——步骤4 2.3.3 消息循环 2.3.4 完成回调函数 2.4 程序编写操作步骤与实验 2.5 小结 第3 章 VC 集成开发环境介绍 3.1 Visual C 开发环境 3.1.1 工作区窗格 3.1.2 输出窗格 3.1.3 编辑区 3.1.4 菜单栏、工具栏、状态栏 3.2 系统菜单功能介绍 3.2.1 File 菜单 3.2.2 Edit 菜单 3.2.3 View 菜单 3.2.4 Insert 菜单 3.2.5 Project 菜单 3.2.6 Build 菜单 3.2.6 Tools 菜单 3.2.7 Window 菜单 3.2.8 Help 菜单 3.3 Visual C 重要工具介绍 3.3.1 C/C 编译器 3.3.2 资源编辑器 3.3.3 资源编译器 3.3.4 链接器和调试器 3.3.5 AppWizard 和ClassWizard 3.3.6 资源浏览器 3.3.7 Microsoft 活动模板库、仓库 3.4 小结 第4 章 MFC 应用程序框架剖析 4.1 什么是MFC 以及MFC 的特点 (例子代码EX04-00) 4.2 MFC 应用程序框架 (例子代码EX04-01) 4.3 应用程序框架说明 4.4 文档-视图体系结构 4.4.1 文档-视图相互作用的函数 4.4.2 单文档-视图应用程序结构 4.4.2 多文档-视图应用程序结构 4.5 MFC 消息映射机制 4.5.1 消息的种类 4.5.2 应用程序的Run 函数 4.5.3 消息映射表 4.5.4 如何添加消息映射 4.6 ClssWizard 的使用 4.6.1 ClssWizard 概貌 4.6.2 如何添加消息处理函数 4.6.3 如何添加成员变量 4.6.4 如何添加一个新类 第5 章 图形与文本 5.1 理解图形设备接口 5.2 设备描述表 5.2.1 什么是设备描述表 5.2.2 MFC 中的设备描述表类 5.2.3 获取设备描述表 5.3 Windows 的GDI 对象 5.4 GDI 对象的创建 5.4.1 自定义画刷(CBrush) 5.4.2 自定义画笔(CPen) 5.4.3 自定义字体(CFont) 5.5 GDI 对象的使用及示例 5.5.1 画笔的使用 5.5.1.1 在MFC 程序中画线 5.5.1.2 在Windows Application 程序中画线 5.5.1.3 实现橡皮筋功能 5.5.2 画刷的使用 5.5.2.1 带颜色的画刷的使用 5.5.2.2 带位图的画刷的使用 5.5.3 字体的使用 5.5.3.1 一个简单的文字处理程序 5.5.3.2 模拟卡拉OK 程序 5.5.3.3 剪切区和路径层 第六章 菜单、工具栏和状态栏 6.1 菜单 6.1.1 菜单介绍 6.1.2 创建一个菜单 6.1.2.1 添加一个菜单资源 6.1.2.2 利用菜单编辑器编辑菜单资源 6.1.2.3 将菜单加入到应用程序中 6.1.2.4 给菜单项添加COMMAND 消息处理 6.1.2.5 给菜单项添加UPDATE_COMMAND_UI 消息处理 6.1.2.6 一个简单的绘图程序 6.1.3 在应用程序中控制菜单 6.1.3.1 在应用程序中取得菜单 6.1.3.2 在应用程序中修改菜单的状态 6.1.3.3 在应用程序中添加、删除、插入菜单或菜单项 6.1.3.4 一个简易的电话本程序 6.1.4 创建快捷方式菜单 6.2 工具栏 6.2.1 工具栏介绍 6.2.1.1 熟悉CToolBar 类 6.2.1.2 AppWizard 是如何创建工具栏 6.2.1.3 利用工具栏编辑器设计工具栏按钮 6.2.2 新建一个工具栏 6.2.3 显示/隐藏工具栏 6.3 状态栏 6.3.1 状态栏介绍 6.3.1.1 熟悉CStatusBar 类 6.3.1.2 AppWizard 是如何创建状态栏 6.3.2 修改状态栏 6.3.2.1 指示器数组 6.3.2.2 修改状态栏窗格 6.3.3 在状态栏上显示鼠标坐标、时钟和进度条 6.3.3.1 在状态栏上显示鼠标坐标 6.3.3.2 在状态栏上显示时钟 6.3.3.3 在状态栏上显示进度条 第七章 对话框和控件 7.1 对话框及控件的介绍 7.1.1 常用控件介绍 7.1.2 对话框介绍 7.1.2.1 对话框的组成 7.1.2.2 对话框的种类 7.1.2.3 使用对话框编辑器设计对话框 7.1.3 创建一个对话框 7.1.3.2 创建非模态对话框 7.1.3.3 对话框的数据交换机制 7.1.3.4 创建模态对话框 7.1.4 模态对话框和非模态对话框的区别 7.1.5 按钮逃跑小程序 7.2 属性页和向导对话框 7.2.1 创建属性页对话框 7.2.1 创建向导对话框 7.3 公用对话框 7.3.1 增加设置对话框来完善绘图程序 7.3.2 颜色对话框的使用 7.3.3 字体对话框的使用 7.3.4 控制控件颜色做漂亮界面 第8 章 文档序列化 8.1 序列化 8.1.1 CArchive 类和Serialize 函数 8.1.2 使自己的类支持序列化 8.1.3 实例:保存和显示图形 8.2 CFile 类 8.2.1 CFile 类的构造函数 8.2.2 打开文件 8.2.3 读写数据 8.2.4 关闭文件 8.3 文件I/O 处理 8.3.1 利用MFC 类来实现 8.3.2 利用C 函数来实现 8.3.3 利用C 函数来实现 8.3.4 利用API 函数来实现 8.3.5 创建保存、打开对话框 8.4 注册表操作 8.4.1 什么是注册表 8.4.2 注册表结构 8.4.3 修改注册表 第9 章 修改框架程序的显示效果 9.1 修改Windows 应用程序外观样式 9.1.1 在框架类中修改程序外观 9.1.2 在视图类中修改程序外观 9.2 制作动画图标 9.3 将图片作为窗口显示的背景 第10 章 网络编程 10.1 计算机网络的基本概念 10.1.1 计算机网络的分类 10.1.2 网络拓扑结构 10.2 网络体系结构和网络协议 10.2.1 ISO/OSI 参考模型 10.2.2 TCP/IP 参考模型 10.2.3 TCP/IP 协议 10.2.3.1 协议概述 10.2.3.2 TCP/IP 协议层次结构及各种协议介绍 10.2.3.3 IP 地址 10.2.3.4 端口号 10.2.4 专业术语解释 10.3 Socket 套接字 10.3.1 Socket 介绍 10.3.2 关于协议族和地址族 10.3.3 使用Socket 10.3.3.1 创建Socket 10.3.3.2 指定本地地址及端口号 10.3.3.3 建立连接 10.3.3.4 监听连接 10.3.3.5 发送数据 10.3.3.6 接收数据 10.3.3.7 关闭套接字 10.3.4 套接字一般调用过程 10.4 WinSock 编程机制 10.4.1 认识Windows Socket 10.4.2 Windows Sockets 库函数介绍 10.4.2.1 Berkeley socket 函数 10.4.2.2 数据库函数 10.4.2.3 Windows 专有扩展函数 10.5 WinSock 编程实例 10.5.1 实例一:面向连接服务的socket 调用 10.5.2 实例二:面向非连接服务的socket 调用 10.5.3 实例三:基于字符界面的聊天程序 第11 章 线程间的同步 11.1 进程和线程的概念 11.2 Win32 的线程 11.2.1 线程的创建 11.2.2 线程的终止 11.2.3 实例:通过创建多线程来编写网络聊天程序 11.3 MFC 的线程处理 11.3.1 创建工作者线程 11.3.2 创建用户界面线程 11.4 线程同步 11.4.1 为什么要同步 11.4.2 等待函数 11.4.3 同步对象 11.4.3.1 关键代码段 11.4.3.2 互斥对象 11.4.3.3 信标对象 11.4.3.4 事件对象 11.4.4 如何选择同步对象 第12 章 进程间的通讯 12.1 进程控制 12.1.1 进程的创建 12.1.2 进程的终止 12.2 进程间通讯 12.2.1 剪贴板通讯方式 12.2.2 邮槽通讯方式 12.2.3 管道通讯方式 12.2.3.1 命名管道通讯 12.2.3.2 匿名管道通讯 第14 章 ActiveX 技术 14.1 ActiveX 简介 14.2 ActiveX 控件和普通Windows 控件 14.2.1 ActiveX 控件和普通Windows 控件的相同点 14.2.2 ActiveX 控件和普通Windows 控件的相同点 14.3 ActiveX 控件的属性、方法和事件 14.3.1 ActiveX 控件的属性 14.3.2 ActiveX 控件的方法 14.3.3 ActiveX 控件的事件 14.4 创建ActiveX 控件
使用自定义View可以让你在Android应用中创建具有自定义外观和交互行为的组件。以下是使用自定义View的基本步骤: 1. 创建一个继承自View或其子类的Java类,作为自定义View的实现。 例如,你可以创建一个名为CustomView的类:`public class CustomView extends View` 2. 在自定义View构造函数中进行初始化和设置。 在构造函数中,你可以设置画笔、颜色、形状、属性等。你还可以处理触摸事件、测量和布局等。 例如,你可以在构造函数中初始化画笔:`Paint paint = new Paint();` 3. 重写onDraw()方法,在该方法中实现自定义View的绘制逻辑。 在onDraw()方法中,你可以使用Canvas对象绘制图形、文本等元素。通过调用invalidate()方法,可以触发View的重新绘制。 例如,你可以在onDraw()方法中使用画笔绘制一个圆:`canvas.drawCircle(x, y, radius, paint);` 4. 根据需要重写其他的方法,处理触摸事件、测量和布局等。 例如,你可以重写onTouchEvent()方法来处理触摸事件,并根据手势改变自定义View的状态。 5. 在XML布局文件或代码中使用自定义View。 如果你希望在XML布局文件中使用自定义View,可以在XML文件中添加相应的标签,并指定自定义View的包名和类名。 例如,你可以在XML布局中添加一个CustomView:`<com.example.app.CustomView android:layout_width="match_parent" android:layout_height="match_parent"/>` 以上是使用自定义View的基本步骤。当你使用自定义View时,可以根据自己的需求定制其外观和行为,并根据需要处理相关事件。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值