Android自定义UI陷阱:LayoutInflater.from().inflate()一定不能工作在父类或虚类里

问题背景:有一些UI具有共性,比如常见的app第一次运行时出现的各种指示框,告诉你往哪搓是调音量的,往哪点是调屏幕亮度的,当点击这些VIew,则其自动消失。或者一动时间后,自动消失。另外一个问题是,不同的方向下加载出来的指示View内容是不一样的。

为此需要将这些特点的View抽象出来,写个父类或者说是基类,为啥一定要这样搞,这样写好处很多。优点如下:

1、可以让代码变得更简洁。每个子View里的共同的方法都由父类来做,每个子View实现自己的逻辑就ok了。

2、因为这些View只工作一次,所以写死在主UI的xml里显得不合时宜,动态添加是最好的。因为牵涉到旋转方向问题,就必须要提前给出这些View的实例化变量名称。如果互相之间是完全是独立的,则需要定义View1 view1, View2 view2...很多个View,然后方向发生变化时挨个通知。如果有个BaseView, View1和View2...都是继承自BaseView,则只需定义BaseView baseView,需要显示时用BaseView实例化具体的是View1 还是View2.如: baseView = new View1(...).然后方向变化时判断baseView是否为空,然后把方向告诉它就ok了。

先来看上面提到的BaseView,这里命名为BaseGuideView:

package org.yanzi.ui;

import org.yanzi.util.OrientationUtil;

import android.content.Context;
import android.view.MotionEvent;
import android.view.View;
import android.widget.RelativeLayout;
import android.widget.TextView;

public abstract  class BaseGuideView extends RelativeLayout implements Rotatable, View.OnClickListener {

	protected int mOrientation = 0;
	protected Context mContext;
	private GuideViewCallback mGuideViewCallback;

	public interface GuideViewCallback{
		public void onGuideViewClick();
	}

	public BaseGuideView(Context context, GuideViewCallback callback) {
		super(context);
		// TODO Auto-generated constructor stub
		mContext = context;
		mGuideViewCallback = callback;
		setOnClickListener(this);
		mOrientation = OrientationUtil.getOrientation();

	}

	@Override
	public void setOrientation(int orientation, boolean animation) {
		// TODO Auto-generated method stub
		mOrientation = orientation;
		requestLayout();
	}

	protected abstract void initView();

	@Override
	public boolean onInterceptTouchEvent(MotionEvent ev) {
		// TODO Auto-generated method stub
		return true; //super.onInterceptTouchEvent(ev)
	}

	@Override
	public void onClick(View v) {
		// TODO Auto-generated method stub
		mGuideViewCallback.onGuideViewClick();
	}

}

最重要一点是我再onInterceptTouchEvent里把点击事件给消费了,这样布局里的孩子就接收不到点击了。然后写了一个GuideViewCallback,当被点击时,会触发onGuideViewClick,这个接口的实现在另一个地方,如集中管理Ui的地方。将这个弹框再消失。另外,就是每次方向发生改变都会执行requestLayout,重新执行view的onMeasure和onLayout.

再定义个NanShiGuide.java继承自上面的类:

package org.yanzi.ui;

import com.example.test1.R;

import android.content.Context;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;

public class NanShiGuide extends BaseGuideView {
	int LAYOUT_ID = R.layout.c_nanshi_guide;
	View guideNanLayout;
	TextView guideNanText;
	public NanShiGuide(Context context, GuideViewCallback callback) {
		super(context, callback);
		// TODO Auto-generated constructor stub
		initView();
	}

	@Override
	protected void initView() {
		// TODO Auto-generated method stub
		Log.i("YanZi", "NanShiGuide initView enter...");
		View v = LayoutInflater.from(mContext).inflate(LAYOUT_ID, this, true);
		guideNanLayout = v.findViewById(R.id.guide_nan_layout);
		guideNanText = (TextView) v.findViewById(R.id.guide_nan_text);
		Log.i("YanZi", "NanShiGuide initView exit...");
	}

}

在这个子类里就可以将资源加载进来了。对应的布局c_nanshi_guide.xml:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
<FrameLayout
    android:id="@+id/guide_nan_layout"
    android:layout_width="200dip"
    android:layout_height="150dip"
    android:background="@drawable/nan1">
    <TextView
        android:id="@+id/guide_nan_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:layout_gravity="bottom|center_horizontal"
        android:textColor="@android:color/white"
        android:textSize="20sp"
        android:text="南公怀瑾."/>

</FrameLayout>    

</RelativeLayout>

在initView函数里将xml加载进来并获得各个控件的实例,我所遇到的问题是,如果这个initView()写在基类(也是个虚类)BaseGuideView的构造函数里,是不能够正常运行的。虽然initView()函数执行了,但是会报错:

07-06 15:17:58.258 I/YanZi   ( 8375): NanShiGuide initView enter...

07-06 15:17:58.258 W/ResourceType( 8375): No package identifier when getting value for resource number 0x00000000

07-06 15:17:58.258 D/AndroidRuntime( 8375): Shutting down VM

07-06 15:17:58.258 W/dalvikvm( 8375): threadid=1: thread exiting with uncaught exception (group=0x410899a8)

找不到package的指针。按理说从java的语法上是完全可以这么用的,虚类调一个虚方法,虚方法由各个子类具体实现,但这里报错了。原因是因为:

View v = LayoutInflater.from(mContext).inflate(LAYOUT_ID, this, true);

这里有个this指针的问题,当initVIew()让虚类调用时,这个this指向谁?是虚类自己还是子类?正因此才挂了,另外这个inflate本身就有一定特殊性,是不能随便乱用this的。我尝试过把BaseGuideView里的initView不写成虚的,而是一个空的函数,依旧是报错。所以遇到这种情况,加载布局一定由各个子View自行加载并初始化是最好的。

效果如下,左上角的图片就是特意显示的,同时将背景变暗:


Android自定义UI陷阱:LayoutInflater.from().inflate()一定不能工作在父类或虚类里

时间: 2024-10-07 06:00:24

Android自定义UI陷阱:LayoutInflater.from().inflate()一定不能工作在父类或虚类里的相关文章

android自定义UI模板图文详解

不知道大家在实际开发中有没有自定义过UI模板?今天花时间研究了一下android中自定义UI模板,与大家分享一下. 每个设计良好的App都是自定义标题栏,在自定义标题栏的过程中大部分人可能都是自定义一个标题的xml文件,然后在需要的地方直接通过include来引用,这比起在每个布局文件中写标题栏已经进化很多了,但仍然不是最简单有效的方法,我们为什么不能自定义一个标题控件呢?今天就带大家自己做一个标题栏控件.效果图如下: 开始啦: 第一步:自定义xml属性 新建一个android项目,在value

Android 自定义UI圆角按钮

Android实际开发中我们一般需要圆角的按钮,一般情况下我们可以让美工做出来相应的按钮图片,然后放上去即可,另外我们可以在布局文件中直接设置,也可以达到一样的效果.下面讲解在布局文件中自定义圆角按钮的小Demo. 代码很简单,实现效果图: 源代码: 源代码: 这里主要是xml布局文件实现: MainActivity: package com.android_drawableresource; import android.app.Activity; import android.os.Bund

Android自定义UI控件(简单方便版,但不灵活)

这种方法的优点就是简单,容易理解,适合开发一些不经常用到的自定义UI控件 缺点就是比较不灵活,如果其他应用想使用这个控件的话得改很多 简单来说,这个方法是用来做成品的,下一篇的方法是用来做模板的. 先看成品,这是一个标题栏控件: 由左右两个按钮和中一个TextView组成: 实现方法: 第一步:定义一个xml文件,用来设计你自定义控件的雏形 示例代码:文件名为title 1 <?xml version="1.0" encoding="utf-8"?> 2

Android自定义UI的实现和应用

在Android项目开发中,不可避免的要遇到自定义的UI,用较好的体验去讨好UED妹子和交互设计师手下留情~几个迭代下来,遇到了不少这样的要求,有简单有复杂.最好的实现方案就是讲业务和UI隔离,封装成独立的UI控件供以后复用.以下列举几个项目中用到的例子,源代码有的已经整理到Github,还有一些需要优化后再整理到GIthub上,暂时先把例子展示出来,待续... 目前的项目:嗨健康.各大应用市场免费哦,欢迎下载体验... O.柱状图动态绘制 这个是为了展示计步历史记录的,分周和月两个展示维度,均

Android自定义UI模板

第一步:自定义xml属性 新建一个android项目,在values文件夹中新建一个atts.xml的文件,在这个xml文件中声明我们一会在使用自定义控件时候需要指明的属性.atts.xml 1 <?xml version="1.0" encoding="utf-8"?> 2 <resources> 3 4 <declare-styleable name="ToolBar"> 5 <attr name=&

android自定义圆角实线边框,圆角虚线边框,直实线,虚实线,半圆角边框

先上图 在现实项目开发中,单纯的Button,EditText等控件远远不能满足我们项目的UI设计需求,这时候,我们就需要自己动手丰衣足食啦.接下来先给大家介绍一些属性,备注写的都非常清楚啦,我就不啰嗦啦. 1 <?xml version="1.0" encoding="utf-8"?> 2 <!--android:shape属性代表绘制的图形形状 retangle:矩形,oval:椭圆 ,line:线 ring,环形--> 3 <sh

android自定义View之NotePad出鞘记

现在我们的手机上基本都会有一个记事本,用起来倒也还算方便,记事本这种东东,如果我想要自己实现,该怎么做呢?今天我们就通过自定义View的方式来自定义一个记事本.OK,废话不多说,先来看看效果图. 整个页面还是很简单的. 1.自定义View的分类 OK,那么在正文开始之前,我想先来说说自定义View的分类,自定义View我们一共分为三类 1.自绘控件 自绘控件就是我们自定义View继承自已有控件,然后扩展其功能,之前两篇自定义View的博客(android自定义View之钟表诞生记,android

Android开发之自定义UI组件和属性

Android系统虽然自带了很多的组件,但肯定满足我们个性化的需求,所以我们为了开发方便,需要自定义Android的UI组件,以实现我们个性化的需求. 自定义组合控件的步骤: 1 .自定一个View,需要继承相对布局,线性布局等ViewGroup的子类.ViewGroup是一个其他控件的容器,能够乘放各种组件. 2 .实现父类的3个构造方法.一般需要在构造方法里始化初自定义布局文件. 一个参数构造方法:为new控件使用 两个参数的造方法:在调用布局文件使用 两个参数的造方法:传递带有样式的布局文

Android自定义TabActivity(实现仿新浪微博底部菜单更新UI)

如今Android上很多应用都采用底部菜单控制更新的UI这种框架,例如新浪微博 点击底部菜单的选项可以更新界面.底部菜单可以使用TabHost来实现,不过用过TabHost的人都知道自定义TabHost究竟是有多麻烦的,原生TabHost的风格是不依附屏幕的底部的,要依附底部就要重写布局. TabHost设置的Container可以管理UI的显示,UI可以用LayoutInflater动态生成,也可以是Activity,但不好管理Activity的生命周期.然后用TabHost控制显示UI的显示