问题背景:有一些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();
}
}
再定义个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...");
}
}
在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)
View v = LayoutInflater.from(mContext).inflate(LAYOUT_ID, this, true);
这里有个this指针的问题,当initVIew()让虚类调用时,这个this指向谁?是虚类自己还是子类?正因此才挂了,另外这个inflate本身就有一定特殊性,是不能随便乱用this的。我尝试过把BaseGuideView里的initView不写成虚的,而是一个空的函数,依旧是报错。所以遇到这种情况,加载布局一定由各个子View自行加载并初始化是最好的。
效果如下,左上角的图片就是特意显示的,同时将背景变暗: