Android N Multi-Window Mode Support

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/weixiao1999/article/details/78178020

1. Multi-Window Mode

  1. 如何进入分屏模式?

    • 长按Overview,App进入Split Screen Mode

    • 单击Overview,点击App标题栏上的吕形按钮,进入Split Screen Mode

    • 单击Overview,长按App标题栏拖入屏幕上的高亮区域,然后进入Freeform Screen Mode

  2. 如何配置App支持分屏模式?

    • 项目首先需要满足的

      targetSdkVersion >= Android N Preview

    • 项目AndroidManifest.xml中需要给MainActivity配置:

      android:resizeableActivity = true

      这个属性值在Android N的手机系统默认true,也就是默认支持分屏,前提是满足条件1。

  3. 名词:

    • Full Screen Mode:全屏幕模式

    • Split Screen Mode: 上下屏幕模式

    • Freeform Screen Mode: 自由尺寸屏幕模式

2. 分屏模式下被禁用的特性

  • 分屏模式下无法隐藏系统的状态栏
  • 分屏模式下无法根据屏幕方向旋转App

3. 分屏模式下Activity的生命周期和回调方法

  1. 正常情况下,切换分屏模式的生命周期

    • 长按overview进入分屏模式的Activity生命周期

      MainActivity: onMultiWindowModeChanged
      MainActivity: isInMultiWindowMode:true
      MainActivity: onPause
      MainActivity: onSaveInstanceState
      MainActivity: onStop
      MainActivity: onDestory
      MainActivity: onCreate
      MainActivity: onStart
      MainActivity: onRestoreInstanceState
      MainActivity: onResume
      //注意这里,这里是因为切换到Split Mode的时候,Activity会先立即失去焦点,这点很关键
      MainActivity: onPause

    • 退出分屏模式

      退出分屏模式时,基于分屏模式下Activity的状态,不同的状态生命周期回调有差异

      如果Activity处于 onPause状态,退出分配模式:

      MainActivity: onSaveInstanceState
      MainActivity: onStop
      MainActivity: onDestory
      MainActivity: onCreate
      MainActivity: onStart
      MainActivity: onRestoreInstanceSate
      MainActivity: onResume
      MainActivity: onPause
      MainActivity: onMultiWindowModeChanged
      MainActivity: isInMultiWindowMode:false
      MainActivity: onResume //这个Log不属于退出分屏流程的回调

      如果Activity处于onResume状态,退出分屏模式:

      MainActivity: onPause
      MainActivity: onSaveInstanceState
      MainActivity: onStop
      MainActivity: onDestroy
      MainActivity: onCreate
      MainActivity: onStart
      MainActivity: onRestoreInstanceState
      MainActivity: onResume
      MainActivity: D/MN: onMultiWindowModeChanged
      MainActivity: isInMultiWindowMode= false

      上面的Log输出说明了,如果退出分屏模式之前为onPause状态,则退出之后Activty也必须切换到onPause状态,然后调用的onResume不属于退出分屏的回调流程,属于Activity展示在前台进程获取焦点时的回调。反之,如果退出之前是onResume状态,则退出之后也必须是onResume,则不必再onResume

      总结就是:分屏模式下做一些操作,譬如退出分屏/改变分屏模式的尺寸,Activty在操作前的状态和操作后的状态要保持一致。至于Activity被放置到前台进程时触发的Activty生命周期回调方法,不属于分屏模式下操作的回调。

    • 分屏模式下,从一个Activity切换到和它同处于多窗口的另外一个Activity

      MainActivity: onPause

      SecondActivity: onResume

    • 分屏模式下,改变窗口的尺寸,也需要判断Activty的上一个状态 onPause/onResume

      操作之前处于onPause状态:

      MainActivity:D/MN: onSaveInstanceState
      MainActivity:onStop
      MainActivity:onDestroy
      MainActivity:onCreate
      MainActivity:onStart
      MainActivity:onRestoreInstanceState
      MainActivity:onResume
      MainActivity:onPause

      操作之前处于onResume状态:

      MainActivity:onPause
      MainActivity:onSaveInstanceState
      MainActivity:onStop
      MainActivity:onDestroy
      MainActivity: onCreate
      MainActivity:onStart
      MainActivity:onRestoreInstanceState
      MainActivity:onResume

    由上面的情况可以得知,当切换到多窗口模式或者改变多窗口模式下Activity 的尺寸时,Activity会被销毁然后重新加载。

  2. 特殊情况下,切换分屏模式的生命周期

    为了解决上述的Activity销毁重建的问题,特做了一下配置:

    1. 在AndroidManifest.xml文件中,给Activity加上属性配置如下:

      android:configChanges="orientation|screenSize|screenLayout|smallestScreenSize"

      • 在Activity中复写方法 onConfigurationChanged (不是必须,根据自身的情况来判断是否需要复写)

    在这种情况下由Full ScreenMode切换为Split Screen Mode,其生命周期:

    MainActivity:onPause
    MainActivity:onSaveInstanceState
    MainActivity:onStop
    MainActivity:onConfigurationChanged
    MainActivity:onMultiWindowModeChanged
    MainActivity:isInMultiWindowMode= true
    MainActivity:onRestart
    MainActivity:onStart
    MainActivity:onResume
    MainActivity:onPause

    但是上面的配置,由Full Screen Mode/Split Screen Mode切换为FreeForm Screen Mode并不适用:

    Split Screen Mode —— >FreeForm Screen Mode

    MainActivity:onPause
    MainActivity:onSaveInstanceState
    MainActivity:onStop
    MainActivity:onDestroy
    MainActivity:onCreate
    MainActivity:onStart
    MainActivity:onRestoreInstanceState
    MainActivity:onResume

    Full Screen Mode —— >FreeForm Screen Mode

    MainActivity:onPause
    MainActivity:onSaveInstanceState
    MainActivity:onStop
    MainActivity:onMultiWindowModeChanged
    MainActivity:isInMultiWindowMode= true
    MainActivity:onDestroy
    MainActivity:onCreate
    MainActivity:onStart
    MainActivity:onRestoreInstanceState
    MainActivity:onResume

    基于上面的配置,当处于Split/FreeForm Mode时,如果改变Activity的尺寸:

    MainActivity:onConfigurationChanged

    除此之外,FreeForm Mode切换为 Full Screen Mode,Activity也会销毁重建:

    MainActivity:onPause
    MainActivity:onSaveInstanceState
    MainActivity:onStop
    MainActivity:onDestroy
    MainActivity: onCreate
    MainActivity:onStart
    MainActivity:onRestoreInstanceState
    MainActivity:onResume
    MainActivity:onMultiWindowModeChanged
    MainActivity:isInMultiWindowMode = false

    当Activity A处于Split Screen Mode时,startActivityForResult Activity SECOND,然后切换为FreeForm Screen Mode,最后Activity SECOND Finish 回到Activity A:

    =========startActivityForResult Activity SECOND=====
    MN - A: onPause
    MN - SECOND: onCreate
    MN - SECOND: onStart
    MN - SECOND: onResume
    MN - A: onSaveInstanceState
    MN - A: onStop
    ============Split Mode —> FreeForm Mode =======
    MN-SECOND: onPause
    MN-SECOND: onSaveInstanceState
    MN-SECOND: onStop
    MN-SECOND: onDestroy
    MN-SECOND: onCreate
    MN-SECOND: onStart
    MN-SECOND: onRestoreInstanceState
    MN-SECOND: onResume
    =========B Finish return A ========
    MN-SECOND: onPause
    MN - A: onDestroy
    MN - A: onCreate
    MN - A: onStart
    MN - A: onRestoreInstanceState
    MN - A: onActivityResult
    MN - A: onResume
    MN-SECOND: onStop
    MN-SECOND: onDestroy

  3. 监测多窗口状态的回调方法

    前提是Android N SDK

    1. boolean Activity.isMultiWindowMode()

      判断Activity是否处于Multi-Window(Split / FreeForm Screen Mode)

    2. void Activity.onMultiWindowModeChanged(boolean isInMultiWindowMode)

      Full Scree Mode <——> window-multi 时触发调用

4. 代码中如何判断App处于哪种模式?

首先在特殊情况下,Full Screen Mode 切换到Split Screen Mode不会销毁重建Activity,而切换到FreeForm Screen Mode会销毁重建Activity。
那么,判断multi-window mode 的条件就是:

1.isMultiWindowMode方法返回值为false,则处于Full Screen Mode

2.isMultiWindowMode方法返回值为true,并且调用了onRestoreInstanceState方法,则处于FreeForm Screen Mode,否则处于Split Screen Mode

5. Layout attributes

在Android N 中我们可以向AndroidManifest.xml 中给activity添加 layout节点,并且设置一些属性,通过这些属性来设置分屏模式的一些行为,如最小尺寸等。

android:defaultWidth
android:defaultHeight
这2个属性是freeform模式下默认的宽度和高度

android:gravity
这个是freeform模式下在手机窗口上默认的Gravity

android:minWidth
android:minHeight
这2个属性freeform模式下最小宽度和高度。

下面是一个示例:

<activity android:name=".SecondActivity">
        <layout
            android:defaultHeight="500dp"
            android:defaultWidth="600dp"
            android:gravity="top|end"
            android:minHeight="450dp"
            android:minWidth="300dp"/>
</activity>

6. Luanch Activity with a defined bounds on Screen

In free-form mode, this activity is to be launched within a defined bounds on screen.
使用这个配置启动的Activity,在由非FreeForm切换为FreeForm模式,则以定义的bounds在屏幕上显示Activity的位置

// Define the bounds in which the Activity will be launched into.
Rect bounds = new Rect(500, 300, 100, 0);

// Set the bounds as an activity option.
ActivityOptions options = ActivityOptions.makeBasic();
options.setLaunchBounds(bounds);

// Start the LaunchBoundsActivity with the specified options
Intent intent = new Intent(this, LaunchBoundsActivity.class);
startActivity(intent, options.toBundle());

7. 测试APP和Android N Multi-window 适配思路

保证APP可以顺利进入/退出分屏模式,且改变APP的尺寸时,UI依然可以非常顺滑,以及在分屏模式下,仍然可以保持性能的稳定性,不会Crash也不会OOM:

  1. 长按Overview之后,确保App能够进入Split Screen Mode分屏模式,且改变尺寸后仍然能正常工作。

  2. 长按Overview,拖动App标题栏进入FreeForm Screen Mode分屏模式,且改变尺寸后仍然能正常工作。

  3. 分屏模式下,在短时间内、多次、迅速的改变APP的尺寸,确保APP没有崩溃,且没有发生内存泄漏,且UI的刷新没有花费太多的时间。

进一步优化分屏模式:

  1. 减少不可滑动的页面和控件
    在分屏过程中,屏幕的高度只有原来的一半,如果有太多的控件不响应滑动事件,那么用户无法上下滑动页面,甚至无法进行下一步操作。
    这类页面属于最常见的Splash screen、登录注册页、弹窗等。

  2. 尽量使用相对位置,以兼容分屏模式下多种窗口尺寸

  3. 尺寸变化时的处理,比如PopUpWindow自定义键盘

8. Talk is cheap , let us see case!

  1. Activity中存在Fragment使用注意

    在分屏模式下,如果Activity被销毁重建,FragmentManager中存在的Fragments会被自动保存,当再次show/ hide 时,需要注意操作的Fragment是否为同一个Fragment,即是否在FragmentManager中存在同一个Fragment,否则show/hide将失效。

    举个栗子:

    代码段一:初始化Fragment

    fragmentArray = new Fragment[] { new xxxFragment(), ...};
    ArrayList<Fragment> fragmentList = (ArrayList<Fragment>) mFragmentManager.getFragments();
    if (fragmentList == null || fragmentList.size() == 0) {
        ...
        transaction.add(R.id.fl_info, fragmentArray[i], fragmentTagArray[i]);
        if (i == mCurrentIndex) {
            transaction.show(fragmentArray[i]);
        } else {
            transaction.hide(fragmentArray[i]);
        }
    }
    

    代码段二:show/hide

    Fragment fragment = fragmentArray[index];
    if (fragment != null) {
        transaction.show(fragmentArray[i]);
    } else {
        ...
    }
    ...
    if (fragment != null) {
        transaction.hide(fragmentArray[i]);
    } else {
        ...
    }
    

    上面的代码中,当Activity销毁重建之后,fragmentList 不为null,无法添加fragmentArray[i],然后show/hide操作时无效。

    解决办法:

    重写Activity 的 onSaveInstanceState方法,并且不执行super.onSaveInstanceState(outState);

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        //super.onSaveInstanceState(outState);
    }
    

    当ViewPager配合Fragment使用时,也需要注意使用,否则会产生切换ViewPager空白。

    根本原因就是操作的Fragment和FragmentManager中存在的不是同一个。

  2. Toast 和 Dialog的使用注意

    首先我们需要了解什么是Window,Window是一个抽象类,它的具体实现是PhoneWindow,其实现过程是在WindowManagerService中。

    Window不能够直接访问,需要借助WindowManager。

    在Android中,所有的视图都是通过Window来呈现,不管是Activity、Dialog还是Toast,他们的视图实际上都是附加在window上的,所以Window是View的实际管理者。

    单击事件由Window传给DecorView,然后传递给我们的View。

    Activity设置视图setContentView也是由Window完成的
    WindowManager.addView(mView);

    WindowManage.LayoutParamas有flags和type参数,其中Type参数表示window的类型,有3种类型,分别是应用window、子window、系统window

    应用window对应一个Activity
    子window不能单独存在,需要附属在特定的父Window之中,比如Dialog
    系统window是需要生命权限才能创建的window,比如Toast和系统状态栏

    Window是分层的,每个window都有对应的z-ordered,层级大的会覆盖在层级小的window上面,在3类window中,应用window层级范围是1-99,子window层级是1000-1999,系统window层级是2000-2999,这些层级范围对应着windowManager.LayoutParamas的Type参数。

    Window是一个抽象的概念,每一个window对应一个View,window和view通过ViewRootImpl联系,所以View是window存在的体现。

    了解了这些,我们来看Dialog和Toast的window创建过程:

    Dialog的window创建过程

    创建window,通过PolicyManager的makeNewWindow方法完成
    初始化DecorView并将Dialog的视图添加到DecorView中
    WindowManager将DecorView添加到window中并显示
    普通的Dialog有特殊之处,那就是必须采用Activity的Context,一般来说Dialog的window依附于Activity的window

    Toast的window创建过程:

    首先Toast也是基于window实现的,但是和Dialog不同,具体的就不详述了
    Toast属于系统window,属于整个屏幕范围的window

    那么,当手机分屏时,如果使用了Toast,则Toast的会保持原位置不变。

    所以为了适配分屏模式,不建议使用Toast,可以使用Dialog代替

  3. PopUpWindow自定义键盘在尺寸变化时如何适配

    在分屏模式下,APP的尺寸产生了变化,PopUpWindow自定义键盘会遮盖掉整个EditText,这显然不是我们想要看到的,我们想要的是系统键盘那样将EditText放在键盘的上方。

    思路很简单,在显示PopUpWindow之前,计算出EditText的bottom应该所在的准确位置,然后得到原位置和理想位置的distance,然后整体布局Move distance,当键盘隐藏,自动回到原位置。

    首先我们需要了解一些东西:

    如何获取屏幕尺寸?

    如何准确获取EditText原位置?

    如何计算Distance,然后Move布局到理想效果?

    获取屏幕尺寸:

    DisplayMetrics dm = null;
    activity.getWindowManager().getDefaultDisplay().getMetrics(dm);
    int screenHeight = dm.heightPixels;
    

    准确获取EditText原位置

    int editTextBottom = 0;
    Rect rect = new Rect();
    //获取view在视图屏幕范围内的坐标,如果view被遮挡,则返回false, rect(0,0,0,0)
    boolean globalVisibleRect = editText.getGlobalVisibleRect(rect);
    if (globalVisibleRect){
        editTextBottom = rect.bottom;
    }
    

    计算Distance并Move blockLayout:

    if(editTextBottom!=0) {
      int scrollY = blockLayout.getScrollY();
      int popUpWindowTop = screenHeight - popUpWindowHeight;
      distance = editTextBottom - (popUpWindowTop);
      if (scrollY + distance < 0) {
            distance = -scrollY;//防止多次向上移动 x * distance之后,无法复位
      }
      if (blockLayout instanceof AutoPopLinearLayout) {
          ((AutoPopLinearLayout) blockLayout).startScroll(scrollY,distance,500);
      }
    }
    

    blockLayout是EditText外层布局LinearLayout:

    public class AutoPopLinearLayout extends LinearLayout {
    
        private final Context context;
        private final Scroller mScroller;
        private boolean isMove;
    
        public AutoPopLinearLayout(Context context, AttributeSet attrs) {
            super(context, attrs);
            this.context = context;
            DecelerateInterpolator interpolator = new DecelerateInterpolator();
            mScroller = new Scroller(context,interpolator);
        }
    
        /**
         * mScroller是一个封装位置和速度等信息的变量.
         * startScroll函数只是对它的一些成员变量做一些记录和计算.
         * 这个函数的结果就是导致mScroller.computeScrollOffset()返回true
         * 以及触发computeScroll方法的调用
         *
         * 布局坐标体系是以左上方为(0,0)
         *
         * @param startY Y方向偏移量,作为开始位置
         * @param dy 移动的Y方向上的距离,dy大于0,则布局往上移动,反之向下
         * @param duration
         */
        public void startScroll(int startY, int dy, int duration) {
            isMove = true;
            mScroller.startScroll(0,startY,0,dy,duration);
            invalidate();
        }
    
        @Override
        public void computeScroll() {
            /*
             * 如果mScroller没有调用startScroll,这里将返回false。
             * scrollTo方法依据封装在mScroller中的位置信息对blockLayout中的childView进行移动
             */
            if (mScroller.computeScrollOffset()) {
                scrollTo(mScroller.getCurrX(),mScroller.getCurrY());
                postInvalidate();
                isMove = true;
            } else {
                isMove = false;
            }
            super.computeScroll();
        }
    
        public boolean isMove() {
            return isMove;
        }
    }
    

    这里主要需要了解的是: Scroller的概念

    Scroller类是为了实现View平滑滚动的一个Helper类,通常是咋自定义View的时候使用。

    如上代码中,mScroller记录和计算View滚动的位置,调用startScroll方法驱动,再重写View的computeScroll(),完成实际的滚动,实际的滚动通过scrollTo方法完成。

    这里需要知道的一些API:

    View.getGlobalVisibleRect(Rect rect)

    顾名思义就是获取view的全局可视左上右下坐标,坐标原点时左上角(0,0),右下方向为正方向
    

    View.getScrollY()

    return the edge of top of the display part of you view
    返回view的可视部分的top边缘位置坐标
    

    scrollTo(x,y)

    scrolled to one postion(x,y)
    

    Scroller.getCurrX()/getCurrY()

    return current new offset X/Y in the scroll
    返回新的偏移位置 X/Y
    
  4. 如果某个Activity不需要支持分屏如何处理

    使用下面代码启动Activity:

    Intent intent = new Intent(this, XXXActivity.class);
    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT);
    startActivity(intent);
    

    并且Activity需要设置属性:

    <activity
        android:resizeableActivity="false"
        android:excludeFromRecents="true"
        android:name=".ThirdActivity">
    </activity>
    

    这样子在新的任务栈中开启Activity,就会以全屏方式打开,并且不支持分屏。其中android:excludeFromRecents="true"是为finish掉Activity之后,清除Recents中的Activity。

  5. 分屏模式下Activity销毁重建时的数据缓存,Temp数据缓存

    主要是Activity的重建缓存机制,涉及到2个方法:

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        Log.d("N","onSaveInstanceState");
    }
    
    @Override
        protected void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);
        Log.d("N","onRestoreInstanceState");
    }
    
  6. 如何适配不可滑动的页面,以及解决滑动时产生的问题

    分屏模式下由于尺寸的变化,一些不可滑动的页面只能显示部分内容,所以针对这些页面需要在布局外层嵌套ScrollView,并且设置android:fillViewport="true"

    <ScrollView
        android:fillViewport="true"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <LinearLayout
            android:orientation="vertical"
            android:gravity="center_horizontal"
            android:layout_width="match_parent"
            android:layout_height="match_parent">       
    
            ...
    
        </LinearLayout>
    
    </ScrollView>
    

    由于ScrollView嵌套之后,导致LinearLayout 的 match_parent不生效,会以wrap_content来计算,在大屏手机如三星手机上显示不全,所以需要设置 android:fillViewport=”true”属性。

    值得注意的是,如果ScrollView嵌套ViewPager的时候,ViewPager无法正确显示高度的,导致内容显示不全,以及ViewPager左右滑动冲突的问题。

    这个时候就需要自定义ViewPager,栗如:

    public class ResetViewPager extends ViewPager {
    
        private int lastX = -1;
        private int lastY = -1;
    
        public ResetViewPager(Context context) {
            super(context);
        }
    
        public ResetViewPager(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    
            heightMeasureSpec = resetHeightMeasureSpec(widthMeasureSpec);
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
    
        /**
         * 重新计算 heightMeasureSpec
         * @param widthMeasureSpec
         * @return
         */
        private int resetHeightMeasureSpec(int widthMeasureSpec) {
            int heightMeasureSpec;
            int height = 0;
            for (int i = 0; i < getChildCount(); i++) {
                View childAt = getChildAt(i);
                childAt.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
                int h = childAt.getMeasuredHeight();
                if (h > height) {
                    height = h;
                }
            }
            heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
            return heightMeasureSpec;
        }
    
        @Override
        public boolean dispatchTouchEvent(MotionEvent ev) {
            int rawX = (int) ev.getRawX();
            int rawY = (int) ev.getRawY();
            int dealtX = 0;
            int dealtY = 0;
            switch (ev.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    dealtX = 0;
                    dealtY = 0;
                    //保证子ViewPager能收到ACTION_MOVE事件
                    getParent().requestDisallowInterceptTouchEvent(true);
                    break;
                case MotionEvent.ACTION_MOVE:
                    dealtX += Math.abs(rawX - lastX);
                    dealtY += Math.abs(rawY - lastY);
                    //这里的拦截判断依据是左右滑动
                    if(dealtX >= dealtY){
                        getParent().requestDisallowInterceptTouchEvent(true);
                    }else{
                        getParent().requestDisallowInterceptTouchEvent(false);
                    }
                    lastX = rawX;
                    lastY = rawY;
                    break;
                case MotionEvent.ACTION_CANCEL:
                    break;
                case MotionEvent.ACTION_UP:
                    break;
            }
    
            return super.dispatchTouchEvent(ev);
    
        }
    }
    
  7. ListView在分屏模式时的使用注意

    示栗:

    代码段一

    @Override
    public int getItemViewType(int position) {
        ...
        return (rpipttyp.equals("00") || rpipttyp.equals("13")) 
                            ? R.layout.item_lv_select_condition2 : R.layout.item_lv_select_condition;
    }
    
    @Override
    public int getViewTypeCount() {
        return 2;
    }
    

    代码段二

    int ViewType = getItemViewType(position);
    switch (ViewType) {
        case R.layout.item_lv_select_condition2:    
            ...
        break;
    
        case R.layout.item_lv_select_condition: 
            ...
        break;
    }
    

    上面的代码是根据 position返回 Item的 Type,以及在 getView() 的时候判断Type

    但是上面的代码写法在分屏模式下会产生异常:

    Exception: ArrayIndexOfBoundsException

    原因是:

    The ListView item view type you are returning from getItemViewType() is < getViewTypeCount()

    也就是说:ListView使用Adapater时,getViewTypeCount() 方法和 getItemViewType() 方法返回值之间有一定的关系。

    如果 getViewTypeCount 返回值为2,那么 getItemViewType(position) 方法的返回值应该为0,1,不能超过1,否则会出现 ArrayIndexOfBoundsException

    栗如:

    @Override
    public int getItemViewType(int position) {
        ...
        return (rpipttyp.equals("00") || rpipttyp.equals("13")) ? 0: 1;
    }
    

9. 附录

猜你喜欢

转载自blog.csdn.net/weixiao1999/article/details/78178020