如何在Swift中qt5 创建自定义控件件

【Android】Android自定义控件详解
开学前提前来校,结束了寒假学习的尾巴工作。现在可以有时间来啃《Android群英传》,所以还是老习惯,在md上做好笔记写好总结。第三章主要讲自定义View。
Activity的setContentView()方法将布局显示到手机屏幕上的实现原理每个Activity都有一个Window对象,这个对象由PhoneWindow来实现,PhoneWindow将DecorView设置为整个应用窗口的根View。DecorView作为窗口界面的顶层视图,通过其封装的一系列方法就可以操作整个窗口。View的测量Android中View的测量在onMeasure()方法中进行,方法接收两个参数int widthMeasureSpec, int heightMeasureSpec,分别表示宽度MeasureSpec对象和高度MeasureSpec对象,这里涉及到MeasureSpec类,MeasureSpec对象是一个32位的int值,其中高2位表示测量的模式,即EXACTY,AT_MOSTT,和UNSPEECIFIED三种:EXACTY表示精确模式,当控件的layout_width和layouut_height属性被指定为具体的数值时,或者指定为match_parent时,系统使用的是EXACTY模式;AT_MOST表示最大值模式,当控件的layout_width和layouut_height属性被指定为wrap_content时,只要空间的尺寸不超过父控件的尺寸,就会随着内容的大小而变化;UNSPEECIFIED表示不指定大小,这种模式通常在自定义View的时候才使用。View类默认的onMeasure()方法只支持EXACTY模式,自定义控件的时候需要重写onMeasure()方法。通过Ctrl+鼠标左键点进去,我们知道onMeasure()方法底层调用的是setMeasuredDimension()方法。所以在onMeasure()方法中,我们需要把测量值传给setMeasuredDimension()方法。然而int widthMeasureSpec, int heightMeasureSpec两个参数都是默认值,所以我们需要根据测量模式来重新确定测量值,就是下面的自定义的measureHeight()方法、measureWidth()方法。代码的解释都已经注释,如下:/** * 下面是自定义的measureHeight()方法、measureWidth()方法实现对宽、高值的自定义 * * @param measureSpec * @return */private int measureWidth(int measureSpec) {
int result = 0;
// MeasureSpec.getMode()提取measureSpec对象中包含的测量模式
int specMode = MeasureSpec.getMode(measureSpec);
// MeasureSpec.getSize()提取measureSpec对象中包含的测量值大小
int specSize = MeasureSpec.getSize(measureSpec);
// 判断测量类型,根据不同的测量模式specMode来指定不同的测量值specSize
if (specMode == MeasureSpec.EXACTLY) {
// 模式为EXACTLY,直接使用指定的specSize
result = specS
// 否则,需要制定默认的测量值为200
result = 200;
// 判断,如果测量模式为AT_MOST,测量值指定为result, specSize中的最小值
if (specMode == MeasureSpec.AT_MOST) {
result = Math.min(result, specSize);
}}/** * measureHeight()方法的含义同上面的measureWidth()一样 * * @param measureSpec * @return */private int measureHeight(int measureSpec) {
int result = 0;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
if (specMode == MeasureSpec.EXACTLY) {
result = specS
result = 200;
if (specMode == MeasureSpec.AT_MOST) {
result = Math.min(result, specSize);
}}所以重写的onMeasure()方法代码就很简单啦,只需要在setMeasuredDimension()方法中调用自定义的measureHeight()方法、measureWidth()方法计算测量值就可以啦。代码如下:@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec));}View的绘制然后简单的重写View类的onDraw()方法就可以在Canvas对象上面绘制自定义View啦。可以看到onDraw(Canvas canvas)方法接收一个Canvas对象,创建Canvas对象代码如下:Canvas canvas = new Canvas(bitmap);具体怎么使用Canvas对象来绘制自定义View,请参考我的另外一篇博客《Android图片资源的加载、简单处理》这里直接贴出代码如下:@Overrideprotected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.GRAY);
int width = getWidth();
int height = getHeight();
Log.d("wondertwo", "width : " + width + " height : " + height);}ViewGroup的测量ViewGroup控件作为父控件可以包含多个View控件,并可以管理它包含的所有View控件。当ViewGroup的大小指定为wrap_content的时候,ViewGroup会对子View进行遍历,通过调用子View的Measure方法获得所有View的大小,从而来决定自己的大小。其他情况时则会通过具体的指定值来设置自己的大小。然后ViewGroup遍历所有字View的Layout方法来把他们放到指定的位置上,自定义View就是通过重写onLayout()方法来实现控制子View显示位置的逻辑。同样,如果需要支持wrap_content属性,还必须重写onMeasure()方法才可以。自定义View通常实现自定义View有三种方法:对现有控件进行拓展通过组合来实现新控件重写View来实现全新控件涉及到View中比较重要的几个回调方法:onFinishInflate()----从XML加载组件后回调onSizeChanged()----组件大小改变时回调onMeasure()----回调该方法来进行View组件的测量onLayout()----回调该方法来显示View组件的显示位置onTouchEvent()----监听触摸事件对现有控件进行拓展:以TextView控件为例来展开说明,TextView之所以能够显示文本信息是因为调用了TextView类的onDraw()方法来把文本信息绘制到View控件上,代码如下所示:@Overrideprotected void onDraw(Canvas canvas) {
super.onDraw(canvas);}我们可以通过在super.onDraw(canvas)的前面或者后面加上我们自己的控制逻辑,来实现在绘制文本信息的前后完成自定义的操作。如下所示:@Overrideprotected void onDraw(Canvas canvas) {
// 回调父类方法之前实现自己的逻辑,对TextView来说实在绘制文本之前
super.onDraw(canvas);
// 回调父类方法之前实现自己的逻辑,对TextView来说实在绘制文本之后}比如我们可以利用Android 中Paint对象的Shader渲染器来实现TextView文字闪动的效果。要实现这个效果首先通过getPaint()方法获取Paint对象mPaint = getPaint(),然后通过mPaint.setShader(mLinearGradient)把定义好的Shader效果通过属性LinearGradient设置给Paint对象。示例代码如下:@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if (mViewWidth == 0) {
mViewWidth = getMeasuredWidth();
if (mViewWidth & 0) {
mPaint = getPaint();
mLinearGradient = new LinearGradient(
mViewWidth,
new int[]{
Color.BLUE, 0xffffffff,
Color.BLUE},
Shader.TileMode.CLAMP);
mPaint.setShader(mLinearGradient);
mGradientMatrix = new Matrix();
}}然后在onDraw()方法中通过矩阵不断平移渐变效果,从而产生动态的闪动效果。onDraw()方法代码如下:@Overrideprotected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mGradientMatrix != null) {
mTranslate += mViewWidth / 5;
if (mTranslate & 2 * mViewWidth) {
mTranslate = -mViewW
mGradientMatrix.setTranslate(mTranslate, 0);
mLinearGradient.setLocalMatrix(mGradientMatrix);
postInvalidateDelayed(100);
创建复合控件这种方式需要继承一个合适的ViewGroup,再给他添加指定功能的控件,从而组合成新的复合控件。并指定一些可配置的属性让它具有更强的扩展性。一般分为定义属性,组合控件,引用模板三步。下面是以TopBar,一个标题栏自定义控件为例。自定义控件的实现思路是:定义属性就是把要自定义的控件的所有的属性都定义在在res/values目录下的attrs.xml配置文件中,然后在控件类TopBar类中获取定义的属性值。组合控件就是通过在TopBar类的构造方法中动态获取要组合的控件的实例化对象。比如按钮、TextView的实例等等,把获取到的定义好的属性值设置给控件对象,并且在TopBar类中定义接口暴露给空间的调用者。引用模板也就是使用我们的自定义控件。只是我们平时是大多都是引用系统提供的系统组件,引用自定义控件和引用系统组件的区别是要定义第三方控件的命名空间,并且要使用包名全路径。在res/values目录下创建一个attrs.xml配置文件来定义相关属性,代码如下:&?xml version="1.0" encoding="utf-8"?&&resources&
declare-styleable标签声明使用自定义属性
name属性确定引用名称
&attr&标签声明具体的自定义属性,比如字体、大小、颜色、背景
format属性指定自定义属性的类型
&declare-styleable&
&attr format="string"/&
&attr format="dimension"/&
&attr format="color"/&
&attr format="color"/&
&attr format="reference|color"/&
&attr format="string"/&
&attr format="color"/&
&attr format="integer|color"/&
&attr format="string"/&
&/declare-styleable&&/resources&以上各个标签的含义是:declare-styleable标签声明使用自定义属性name属性确定引用名称标签声明具体的自定义属性,比如字体、大小、颜色、背景format属性指定自定义属性的类型然后是创建TopBar控件类继承Relativelayout。在其构造方法中实现组合控件的逻辑。TopBar控件类代码如下:package com.wondertwo.app.import android.content.Cimport android.content.res.TypedAimport android.graphics.drawable.Dimport android.util.AttributeSimport android.view.Gimport android.view.Vimport android.widget.Bimport android.widget.RelativeLimport android.widget.TextV/** * Created by Allenieo on . */public class TopBar extends RelativeLayout {
// 包含topbar上的元素:左按钮、右按钮、标题
private Button mLeftButton, mRightB
private TextView mTitleV
// 布局属性,用来控制组件元素在ViewGroup中的位置
private LayoutParams mLeftParams, mTitlepParams, mRightP
// 左按钮的属性值,即我们在atts.xml文件中定义的属性
private int mLeftTextC
private Drawable mLeftB
private String mLeftT
// 右按钮的属性值,即我们在atts.xml文件中定义的属性
private int mRightTextC
private Drawable mRightB
private String mRightT
// 标题的属性值,即我们在atts.xml文件中定义的属性
private float mTitleTextS
private int mTitleTextC
private String mT
// 映射传入的接口对象
private topbarClickListener mL
public TopBar(Context context) {
super(context);
public TopBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
public TopBar(Context context, AttributeSet attrs) {
super(context, attrs);
setBackgroundColor(0xFFF59563);
// 通过这个方法,将在attrs中定义的declare-styleable的所有属性的值存储到TypedArray中
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.TopBar);
// 从TypedArray中取出相应的值来为要设置的属性赋值
mLeftTextColor = ta.getColor(R.styleable.TopBar_leftTextColor, 0);
mLeftBackground = ta.getDrawable(R.styleable.TopBar_leftBackground);
mLeftText = ta.getString(R.styleable.TopBar_leftText);
mRightTextColor = ta.getColor(R.styleable.TopBar_rightTextColor, 0);
mRightBackground = ta.getDrawable(R.styleable.TopBar_rightBackground);
mRightText = ta.getString(R.styleable.TopBar_rightText);
mTitleTextSize = ta.getDimension(R.styleable.TopBar_titleTextSize, 10);
mTitleTextColor = ta.getColor(R.styleable.TopBar_titleTextColor, 0);
mTitle = ta.getString(R.styleable.TopBar_title);
// 获取完TypedArray的属性值后,一般要调用recyle方法来避免重新创建的时候发生错误
ta.recycle();
// 开始组合控件
mLeftButton = new Button(context);
mRightButton = new Button(context);
mTitleView = new TextView(context);
// 用我们引用的属性值来为左、右的点击按钮和中间的标题栏三个组件元素赋值
mLeftButton.setTextColor(mLeftTextColor);
mLeftButton.setBackground(mLeftBackground);
mLeftButton.setText(mLeftText);
mRightButton.setTextColor(mLeftTextColor);
mRightButton.setBackground(mRightBackground);
mRightButton.setText(mRightText);
mTitleView.setText(mTitle);
mTitleView.setTextColor(mTitleTextColor);
mTitleView.setTextSize(mTitleTextSize);
mTitleView.setGravity(Gravity.CENTER);
// 为组件元素设置相应的布局元素
mLeftParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
mLeftParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT, TRUE);
// 添加到ViewGroup
addView(mLeftButton, mLeftParams);
mRightParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
mRightParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT, TRUE);
addView(mRightButton, mRightParams);
mTitlepParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
mTitlepParams.addRule(RelativeLayout.CENTER_IN_PARENT, TRUE);
addView(mTitleView, mTitlepParams);
// 按钮的点击事件,不需要具体的实现,只需调用接口的方法,回调时再具体去实现
mLeftButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
mListener.leftClick();
mRightButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
mListener.rightClick();
* 设置按钮的显示与否,通过id区分按钮,flag区分是否显示
* @param id
* @param flag
public void setButtonVisable(int id, boolean flag) {
if (flag) {
if (id == 0) {
mLeftButton.setVisibility(View.VISIBLE);
mRightButton.setVisibility(View.VISIBLE);
if (id == 0) {
mLeftButton.setVisibility(View.GONE);
mRightButton.setVisibility(View.GONE);
* 暴露一个方法给调用者来注册接口回调,通过接口来获得回调者对接口方法的实现
* @param mListener
public void setOntopbarClickListener(topbarClickListener mListener) {
this.mListener = mL
* 接口对象,实现回调机制,在回调方法中通过映射的接口对象调用接口中的
* 方法,而不用考虑具体实现,具体实现由回调者自己去实现
public interface topbarClickListener {
// 左按钮点击事件
void leftClick();
// 右按钮点击事件
void rightClick();
}}组合控件要完成三件事情:获取定义好的属性,比如颜色,字体,等属性值;创建要添加到自定义控件中的组件的实例化对象,比如Button,TextView等等组件。并把获取的属性值设置给这些组件的实例化对象;封装接口,让自定义控件的调用者去实现具体逻辑;系统提供了TypedArray这个api来获取自定义属性集,R.styleable.TopBar就是attrs文件&declare-styleable name= "TopBar"&标签指定的name。获取代码如下:TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.TopBar);先来看看TopBar类中定义的接收变量有哪些:// 包含topbar上的元素:左按钮、右按钮、标题private Button mLeftButton, mRightBprivate TextView mTitleV// 布局属性,用来控制组件元素在ViewGroup中的位置private LayoutParams mLeftParams, mTitlepParams, mRightP// 左按钮的属性值,即我们在atts.xml文件中定义的属性private int mLeftTextCprivate Drawable mLeftBprivate String mLeftT// 右按钮的属性值,即我们在atts.xml文件中定义的属性private int mRightTextCprivate Drawable mRightBprivate String mRightT// 标题的属性值,即我们在atts.xml文件中定义的属性private float mTitleTextSprivate int mTitleTextCprivate String mT// 映射传入的接口对象private topbarClickListener mL再看看TopBar类的完整代码如下:package com.wondertwo.app.import android.content.Cimport android.content.res.TypedAimport android.graphics.drawable.Dimport android.util.AttributeSimport android.view.Gimport android.view.Vimport android.widget.Bimport android.widget.RelativeLimport android.widget.TextV/** * Created by Allenieo on . */public class TopBar extends RelativeLayout {// 包含topbar上的元素:左按钮、右按钮、标题private Button mLeftButton, mRightBprivate TextView mTitleV// 布局属性,用来控制组件元素在ViewGroup中的位置private LayoutParams mLeftParams, mTitlepParams, mRightP// 左按钮的属性值,即我们在atts.xml文件中定义的属性private int mLeftTextCprivate Drawable mLeftBprivate String mLeftT// 右按钮的属性值,即我们在atts.xml文件中定义的属性private int mRightTextCprivate Drawable mRightBprivate String mRightT// 标题的属性值,即我们在atts.xml文件中定义的属性private float mTitleTextSprivate int mTitleTextCprivate String mT// 映射传入的接口对象private topbarClickListener mLpublic TopBar(Context context) {
super(context);}public TopBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);}public TopBar(Context context, AttributeSet attrs) {
super(context, attrs);
setBackgroundColor(0xFFF59563);
// 通过这个方法,将在attrs中定义的declare-styleable的所有属性的值存储到TypedArray中
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.TopBar);
// 从TypedArray中取出相应的值来为要设置的属性赋值
mLeftTextColor = ta.getColor(R.styleable.TopBar_leftTextColor, 0);
mLeftBackground = ta.getDrawable(R.styleable.TopBar_leftBackground);
mLeftText = ta.getString(R.styleable.TopBar_leftText);
mRightTextColor = ta.getColor(R.styleable.TopBar_rightTextColor, 0);
mRightBackground = ta.getDrawable(R.styleable.TopBar_rightBackground);
mRightText = ta.getString(R.styleable.TopBar_rightText);
mTitleTextSize = ta.getDimension(R.styleable.TopBar_titleTextSize, 10);
mTitleTextColor = ta.getColor(R.styleable.TopBar_titleTextColor, 0);
mTitle = ta.getString(R.styleable.TopBar_title);
// 获取完TypedArray的属性值后,一般要调用recyle方法来避免重新创建的时候发生错误
ta.recycle();
// 开始组合控件
mLeftButton = new Button(context);
mRightButton = new Button(context);
mTitleView = new TextView(context);
// 用我们引用的属性值来为左、右的点击按钮和中间的标题栏三个组件元素赋值
mLeftButton.setTextColor(mLeftTextColor);
mLeftButton.setBackground(mLeftBackground);
mLeftButton.setText(mLeftText);
mRightButton.setTextColor(mLeftTextColor);
mRightButton.setBackground(mRightBackground);
mRightButton.setText(mRightText);
mTitleView.setText(mTitle);
mTitleView.setTextColor(mTitleTextColor);
mTitleView.setTextSize(mTitleTextSize);
mTitleView.setGravity(Gravity.CENTER);
// 为组件元素设置相应的布局元素
mLeftParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
mLeftParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT, TRUE);
// 添加到ViewGroup
addView(mLeftButton, mLeftParams);
mRightParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
mRightParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT, TRUE);
addView(mRightButton, mRightParams);
mTitlepParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
mTitlepParams.addRule(RelativeLayout.CENTER_IN_PARENT, TRUE);
addView(mTitleView, mTitlepParams);
// 按钮的点击事件,不需要具体的实现,只需调用接口的方法,回调时再具体去实现
mLeftButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
mListener.leftClick();
mRightButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
mListener.rightClick();
});}/** * 设置按钮的显示与否,通过id区分按钮,flag区分是否显示 * * @param id * @param flag */public void setButtonVisable(int id, boolean flag) {
if (flag) {
if (id == 0) {
mLeftButton.setVisibility(View.VISIBLE);
mRightButton.setVisibility(View.VISIBLE);
if (id == 0) {
mLeftButton.setVisibility(View.GONE);
mRightButton.setVisibility(View.GONE);
}}/** * 暴露一个方法给调用者来注册接口回调,通过接口来获得回调者对接口方法的实现 * * @param mListener */public void setOntopbarClickListener(topbarClickListener mListener) {
this.mListener = mL}/** * 接口对象,实现回调机制,在回调方法中通过映射的接口对象调用接口中的 * 方法,而不用考虑具体实现,具体实现由回调者自己去实现 */
public interface topbarClickListener {
// 左按钮点击事件
void leftClick();
// 右按钮点击事件
void rightClick();
}}值得注意的是,在暴露接口时我们只把接口中未实现的方法(按钮点击事件)的具体实现逻辑交给实现类,也就是自定义控件的调用者去做就好。接口方法的回调依然由接口的映射对象在TopBar类中调用。而接口的映射对象只考虑调用接口中的方法而不用去考虑如何实现,使得我们的自定义控件本身和它的调用者之间完全解耦,其实这也是作为一个控件模板最基本的要求。那么分开来看一下代码是怎样实现这个思路的。在TopBar类中定义公有抽象接口,代码如下:
/** * 接口对象,实现回调机制,通过映射的接口对象调用接口中的 * 方法,而不用考虑具体实现,具体实现由回调者自己去实现 */public interface topbarClickListener {
// 左按钮点击事件
void leftClick();
// 右按钮点击事件
void rightClick();}然后暴露一个方法给调用者来实现接口的抽象方法,也就是按钮的点击监听事件的具体处理逻辑。代码如下:
/** * 暴露一个方法给调用者来实现接口的抽象方法,也就是按钮的点击监听 * * @param mListener */public void setOntopbarClickListener(topbarClickListener mListener) {
this.mListener = mL}调用者在具体的实现类中就是通过mTopbar = (TopBar) findViewById(R.id.topBar)得到TopBar控件的实例,然后由TopBar控件的实例调用上面的setOntopbarClickListener(topbarClickListener mListener)方法实现接口抽象方法的具体处理逻辑的。我们来看一下实现类的代码如下:
// 获得我们创建的TopBar对象
mTopbar = (TopBar) findViewById(R.id.topBar);
mTopbar.setOntopbarClickListener(new TopBar.topbarClickListener() {
public void leftClick() {
Toast.makeText(TopbarTestActivity.this, "left", Toast.LENGTH_SHORT).show();
public void rightClick() {
Toast.makeText(TopbarTestActivity.this, "right", Toast.LENGTH_SHORT).show();
});然后接口方法的回调依然由接口的映射对象在TopBar类中调用。具体到我们的自定义控件,回调接口方法的接口映射对象就是mLeftButton和mRightButton这两个按钮。回调的代码在TopBar类的构造方法中的最后。代码如下:
// 按钮的点击事件,不需要具体的实现,只需调用接口的方法,回调时再具体去实现
mLeftButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
mListener.leftClick();
mRightButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
mListener.rightClick();
});另外,我们还在TopBar类中封装了设置按钮显示与否的方法setButtonVisable(int id, boolean flag)。代码如下:
/** * 设置按钮的显示与否,通过id区分按钮,flag区分是否显示 * * @param id * @param flag */public void setButtonVisable(int id, boolean flag) {
if (flag) {
if (id == 0) {
mLeftButton.setVisibility(View.VISIBLE);
mRightButton.setVisibility(View.VISIBLE);
if (id == 0) {
mLeftButton.setVisibility(View.GONE);
mRightButton.setVisibility(View.GONE);
}}setButtonVisable(int id, boolean flag)方法还是由控件调用者在具体的实现类中通过控件的实例去调用。调用的代码如下:
// 设置按钮的显示状态
mTopbar.setButtonVisable(0, true);
mTopbar.setButtonVisable(1, true);到这里自定义控件的封装可以说已经完成了。接下来就是使用我们自定义的控件TopBar了,是不是有点小激动呢?你只要把它当做一个普通的控件去使用就好了,就和平时使用Button、TextView、ListView这些控件一样去使用就好。区别有以下两点:需要为自定义控件创建命名空间;在声明自定义控件时需要制定完整的包名全路径;为自定义孔家创建命名空间很简单,代码如下:xmlns:custom="/apk/res-auto"其中xmlns就是在指定命名空间,这里的命名空间指定为custom。其他部分格式都是固定不变的,/apk/res-auto这么地址就便是自定义控件。所以当调用者引入第三方控件,也就是自定义控件,的时候是通过命名空间来引用的。最后也是最重要的就是怎样使用自定义控件了。使用方法和系统控件一样,只是要求我们在标签上使用控件的完整包名全路径名即可,是不是很简单呢?代码如下:
&?xml version="1.0" encoding="utf-8"?&&com.wondertwo.app.application.TopBar xmlns:android="/apk/res/android"
xmlns:custom="/apk/res-auto"
android:id="@+id/topBar"
android:layout_width="match_parent"
android:layout_height="40dp"
custom:leftBackground="@drawable/blue_button"
custom:leftText="Back"
custom:leftTextColor="#FFFFFF"
custom:rightBackground="@drawable/blue_button"
custom:rightText="More"
custom:rightTextColor="#FFFFFF"
custom:title="自定义标题"
custom:titleTextColor="#123412"
custom:titleTextSize="15sp"&&/com.wondertwo.app.application.TopBar&
最新教程周点击榜
微信扫一扫}

我要回帖

更多关于 qt5 创建自定义控件 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信