分享

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

 quasiceo 2014-10-09

问题背景:有一些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:

  1. package org.yanzi.ui;  
  2.   
  3. import org.yanzi.util.OrientationUtil;  
  4.   
  5. import android.content.Context;  
  6. import android.view.MotionEvent;  
  7. import android.view.View;  
  8. import android.widget.RelativeLayout;  
  9. import android.widget.TextView;  
  10.   
  11. public abstract  class BaseGuideView extends RelativeLayout implements Rotatable, View.OnClickListener {  
  12.   
  13.     protected int mOrientation = 0;  
  14.     protected Context mContext;  
  15.     private GuideViewCallback mGuideViewCallback;  
  16.   
  17.     public interface GuideViewCallback{  
  18.         public void onGuideViewClick();  
  19.     }  
  20.   
  21.     public BaseGuideView(Context context, GuideViewCallback callback) {  
  22.         super(context);  
  23.         // TODO Auto-generated constructor stub  
  24.         mContext = context;  
  25.         mGuideViewCallback = callback;  
  26.         setOnClickListener(this);  
  27.         mOrientation = OrientationUtil.getOrientation();  
  28.           
  29.     }  
  30.   
  31.   
  32.     @Override  
  33.     public void setOrientation(int orientation, boolean animation) {  
  34.         // TODO Auto-generated method stub  
  35.         mOrientation = orientation;  
  36.         requestLayout();  
  37.     }  
  38.   
  39.     protected abstract void initView();  
  40.   
  41.     @Override  
  42.     public boolean onInterceptTouchEvent(MotionEvent ev) {  
  43.         // TODO Auto-generated method stub  
  44.         return true//super.onInterceptTouchEvent(ev)  
  45.     }  
  46.   
  47.     @Override  
  48.     public void onClick(View v) {  
  49.         // TODO Auto-generated method stub  
  50.         mGuideViewCallback.onGuideViewClick();  
  51.     }  
  52.   
  53. }  

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

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

  1. package org.yanzi.ui;  
  2.   
  3. import com.example.test1.R;  
  4.   
  5. import android.content.Context;  
  6. import android.util.Log;  
  7. import android.view.LayoutInflater;  
  8. import android.view.View;  
  9. import android.widget.TextView;  
  10.   
  11. public class NanShiGuide extends BaseGuideView {  
  12.     int LAYOUT_ID = R.layout.c_nanshi_guide;  
  13.     View guideNanLayout;  
  14.     TextView guideNanText;  
  15.     public NanShiGuide(Context context, GuideViewCallback callback) {  
  16.         super(context, callback);  
  17.         // TODO Auto-generated constructor stub  
  18.         initView();  
  19.     }  
  20.   
  21.     @Override  
  22.     protected void initView() {  
  23.         // TODO Auto-generated method stub  
  24.         Log.i("YanZi""NanShiGuide initView enter...");  
  25.         View v = LayoutInflater.from(mContext).inflate(LAYOUT_ID, thistrue);  
  26.         guideNanLayout = v.findViewById(R.id.guide_nan_layout);  
  27.         guideNanText = (TextView) v.findViewById(R.id.guide_nan_text);  
  28.         Log.i("YanZi""NanShiGuide initView exit...");  
  29.     }  
  30.   
  31. }  

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

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <RelativeLayout xmlns:android="http://schemas./apk/res/android"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="match_parent" >  
  5. <FrameLayout   
  6.     android:id="@+id/guide_nan_layout"  
  7.     android:layout_width="200dip"  
  8.     android:layout_height="150dip"  
  9.     android:background="@drawable/nan1">  
  10.     <TextView   
  11.         android:id="@+id/guide_nan_text"  
  12.         android:layout_width="wrap_content"  
  13.         android:layout_height="wrap_content"  
  14.         android:gravity="center"  
  15.         android:layout_gravity="bottom|center_horizontal"  
  16.         android:textColor="@android:color/white"  
  17.         android:textSize="20sp"  
  18.         android:text="南公怀瑾."/>  
  19.       
  20. </FrameLayout>      
  21.   
  22. </RelativeLayout>  
在initView函数里将xml加载进来并获得各个控件的实例,我所遇到的问题是,如果这个initView()写在基类(也是个虚类)BaseGuideView的构造函数里,是不能够正常运行的。虽然initView()函数执行了,但是会报错:

  1. 07-06 15:17:58.258 I/YanZi   ( 8375): NanShiGuide initView enter...  
  2.   
  3. 07-06 15:17:58.258 W/ResourceType( 8375): No package identifier when getting value for resource number 0x00000000  
  4.   
  5. 07-06 15:17:58.258 D/AndroidRuntime( 8375): Shutting down VM  
  6.   
  7. 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不写成虚的,而是一个空的函数,依旧是报错。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多