精华内容
下载资源
问答
  • setadapter用法
    2019-07-02 09:53:18

    对于一个初学者来说,最需要掌握的技能之一是 ListView 加载布局,不管是同一种布局还是不同的布局,又或者是加载头文件或者加载底部文件。这些掌握起来不是很难,而对于一个中级的搬砖的来说,这些就太过于肤浅了,如果不懂他的原理,那你就只能在一个叫做万恶的 Adapter 上各种吃亏了。那么请允许我来带你揭开它的神秘面纱,能力一般,水平有限,有不对的请告诉我。

    ListView 的用法以及为什么 ListView 要这么设计,要使用一个 Adapter 来展示布局,就不细说了,总之一句话,就是为了更好地拓展。

    废话少说,进入正题,直接从 setAdapter 方法分析,上源码:

        @Override
        public void setAdapter(ListAdapter adapter) {
            //清空数据操作
            if (mAdapter != null && mDataSetObserver != null) {
                mAdapter.unregisterDataSetObserver(mDataSetObserver);
            }
    
            ...
    
            if (mAdapter != null) {
                mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();
                mOldItemCount = mItemCount;
                mItemCount = mAdapter.getCount();
                checkFocus();
    
                mDataSetObserver = new AdapterDataSetObserver();
                mAdapter.registerDataSetObserver(mDataSetObserver);
    
                mRecycler.setViewTypeCount(mAdapter.getViewTypeCount());
    
                int position;
                if (mStackFromBottom) {
                    position = lookForSelectablePosition(mItemCount - 1, false);
                } else {
                    position = lookForSelectablePosition(0, true);
                }
                setSelectedPositionInt(position);
                setNextSelectedPositionInt(position);
    
                if (mItemCount == 0) {
                    // Nothing selected
                    checkSelectionChanged();
                }
            } else {
                mAreAllItemsSelectable = true;
                checkFocus();
                // Nothing selected
                checkSelectionChanged();
            }
    
            requestLayout();
        }

    省略了一部分,省略号上边的主要是清空数据的操作,还有我们发现源码 ListView 设置的 Adapter 是一个观察者模式啊,我们继续往下看,其实关键代码只有 10 - 13 行 和第 19 行 还有第 41 行代码,下边一一说明:

    (1) mOldItemCount:数据改变前的item数量

    (2) mItemCount 所有item的数量

    (3) mRecycler.setViewTypeCount(mAdapter.getViewTypeCount());  mAdapter.getViewTypeCount() 是不是很眼熟,没错,就是设置 View 显示类型的

    (4) requestLayout 方法,就是我们要继续往下看的方法了,他便是绘制 ListView 视图的方法。 

    我们继续往下看源码,接来下看 requestLayout 源码,点击进入这个方法,我们发现这个方法实际上是在 AbsListView 中的犯法,而最终调的确是 View 中的方法,源码如下:

    @CallSuper
    public void requestLayout() {
        if (mMeasureCache != null) mMeasureCache.clear();
    
        if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == null) {
            // Only trigger request-during-layout logic if this is the view requesting it,
            // not the views in its parent hierarchy
            //如果当前的整个View树在进行布局流程的话,则会调用requestLayoutDuringLayout()
            //让这次的布局延时执行  反正我是没理解
            ViewRootImpl viewRoot = getViewRootImpl();
            if (viewRoot != null && viewRoot.isInLayout()) {
                if (!viewRoot.requestLayoutDuringLayout(this)) {
                    return;
                }
            }
            mAttachInfo.mViewRequestingLayout = this;
        }
    
       //PFLAG_FORCE_LAYOUT会在执行View的measure()和layout()方法时判断
        //只有设置过该标志位,才会执行measure()和layout()流程
        mPrivateFlags |= PFLAG_FORCE_LAYOUT;
        mPrivateFlags |= PFLAG_INVALIDATED;
        //自己布局  则调用父类的requestLayout 开始布局
        if (mParent != null && !mParent.isLayoutRequested()) {
            mParent.requestLayout();
        }
        if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == this) {
            mAttachInfo.mViewRequestingLayout = null;
        }
    }

    该方法主要是设置了 PFLAG_FORCE_LAYOUT 和 PFLAG_INVALIDATED 到当前 View 的 Flag 中,这个过程,就是设置当前View 标志位后,就不断的向上调用父布局 View 的 requestLayout(),最后调用到根 View 即 DecorView 的 requestLayout()。然后调用父 View 的 requestLayout 方法,但是我们进入父 View (ViewParent)  发现他就是一个接口,没有具体的实现啊,所以我们需要去找它的实现类,ViewParent 的实现类是 ViewRootImpl,我们来看下 ViewRootImpl 中 requestLayout 的代码:

    @Override
    public void requestLayout() {
      //该boolean变量会在ViewRootImpl.performLayout()开始时置为ture,结束置false
        //表示当前不处于Layout过程
        if (!mHandlingLayoutInLayoutRequest) {
            checkThread();
            mLayoutRequested = true;
            scheduleTraversals();
        }
    }

    如果当前没有进行 layout 过程,则会调用 scheduleTraversals() 方法,进入ViewRootImpl.scheduleTraversals()。

       void scheduleTraversals() {
            if (!mTraversalScheduled) {
           //在下一段代码处会置回false
           //表示在排好这次绘制请求前,不再排其它的绘制请求
                mTraversalScheduled = true;
                mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
                mChoreographer.postCallback(
                        Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
                if (!mUnbufferedInputDispatch) {
                    scheduleConsumeBatchedInput();
                }
                notifyRendererOfFramePending();
                pokeDrawLockIfNeeded();
            }
        }
    

    mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); 我们来解读下这行代码,我们发现在这里调用了一个重要的变量 mChoreographer,它是 Choreographer 类型的,这个对象会请求 Vsync 信号来控制绘制的进行,实现了按帧进行绘制的机制。该方法对于绘制的请求经过了 Choreographer 的编排后,最终会调用回 ViewRootImpl.doTraversal() 方法。

        final TraversalRunnable mTraversalRunnable = new TraversalRunnable(); 
    
        final class TraversalRunnable implements Runnable {
            @Override
            public void run() {
                doTraversal();
            }
        }
    
        void doTraversal() {
            if (mTraversalScheduled) {
                mTraversalScheduled = false;
                mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
    
                if (mProfile) {
                    Debug.startMethodTracing("ViewAncestor");
                }
    
                performTraversals();
    
                if (mProfile) {
                    Debug.stopMethodTracing();
                    mProfile = false;
                }
            }
        }

    根据源码我们发现最后调用的是 performTraversals 方法,自此 ListView 的 measure、layout 和 draw 过程就开始了。

    本文只是解读 setAdapter 方法,至于  ListView 的绘制过程和 recycleBin 机制会在后文解答。

    更多相关内容
  • RecyclerView源码学习笔记(二)setAdapter

    千次阅读 2018-04-02 15:39:26
    引言 上篇文章RecyclerView源码学习笔记(一)构造函数和setLayoutManager方法主要学习了RecyclerView初始...按照我们平时最简单的使用习惯,在调用完setLayoutManager方法之后就要调用setAdapter方法了,直接贴源码...

    引言

    上篇文章RecyclerView源码学习笔记(一)构造函数和setLayoutManager方法主要学习了RecyclerView初始化和setLayoutManager方法的源码,这篇我们学习setAdapter方法的源码

    内容

    setAdapter方法

    按照我们平时最简单的使用习惯,在调用完setLayoutManager方法之后就要调用setAdapter方法了,直接贴源码

        /**
         * Set a new adapter to provide child views on demand.
         * <p>
         * When adapter is changed, all existing views are recycled back to the pool. If the pool has
         * only one adapter, it will be cleared.
         *
         * @param adapter The new adapter to set, or null to set no adapter.
         * @see #swapAdapter(Adapter, boolean)
         */
        public void setAdapter(@Nullable Adapter adapter) {
            // bail out if layout is frozen
            setLayoutFrozen(false);
            setAdapterInternal(adapter, false, true);
            processDataSetCompletelyChanged(false);
            requestLayout();
        }

    代码不多,从注释来看这个方法的作用是

    • 设置一个新的Adapter
    • 所有已经存在的view将会被回收到pool中,如果pool只有一个adapter,那么这个pool将会被清空

    上面提到的pool就是在前一篇RecyclerView源码学习笔记(一)构造函数和setLayoutManager方法中说的RecycledViewPool。
    接下来一行一行看下去。第一行是调用了setLayoutFrozen(false),这个方法的作用是什么呢?从注释来看可以将此方法归纳为以下几点:

    • 决定RecyclerView是否可以进行layout和scroll,如果参数是true,相当于这个RecyclerView被冻住了,那么layout的请求将被推迟,直到调用setLayoutFrozen(false)
    • 当RecyclerView被冻住的时候,RecyclerView的smoothScrollByscrollByscrollToPositionsmoothScrollToPosition这些方法的调用将直接被丢弃,也就是直接返回,这个我们可以看一下scrollBy的源码,当判断到mLayoutFrozen等于true的时候就直接返回了,而这个mLayoutFrozen就是在setLayoutFrozen方法中被赋值的。
        public void smoothScrollBy(@Px int dx, @Px int dy, @Nullable Interpolator interpolator) {
            if (mLayout == null) {
                Log.e(TAG, "Cannot smooth scroll without a LayoutManager set. "
                        + "Call setLayoutManager with a non-null argument.");
                return;
            }
            if (mLayoutFrozen) {
                return;
            }
            if (!mLayout.canScrollHorizontally()) {
                dx = 0;
            }
            if (!mLayout.canScrollVertically()) {
                dy = 0;
            }
            if (dx != 0 || dy != 0) {
                mViewFlinger.smoothScrollBy(dx, dy, interpolator);
            }
        }
    • 另外RecyclerView的TouchEvents 和 GenericMotionEvents事件也会被丢弃,LayoutManager的onFocusSearchFailed不会被调用,但是LayoutManager的scrollToPositionsmoothScrollToPosition并不受影响,还可以照常运行。
      -setAdapter方法和swapAdapter方法会自动结束冰冻状态,就是会调用setLayoutFrozen(false)
    • 任何正在跑的ItemAnimator不会自动停止,需要调用者去手动停止。

    写了这么多,还不如直接看源码来的实在,那么就上源码:

    public void setLayoutFrozen(boolean frozen) {
            if (frozen != mLayoutFrozen) {
                assertNotInLayoutOrScroll("Do not setLayoutFrozen in layout or scroll");
                if (!frozen) {
                    mLayoutFrozen = false;
                    if (mLayoutWasDefered && mLayout != null && mAdapter != null) {
                        requestLayout();
                    }
                    mLayoutWasDefered = false;
                } else {
                    final long now = SystemClock.uptimeMillis();
                    MotionEvent cancelEvent = MotionEvent.obtain(now, now,
                            MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
                    onTouchEvent(cancelEvent);
                    mLayoutFrozen = true;
                    mIgnoreMotionEventTillDown = true;
                    stopScroll();
                }
            }
        }

    首先会判断当前状态和目标状态是否一样,一样的话就直接返回了,所以我们平时在使用的时候就不需要再自己去判断了。
    当然我们这里肯定是要不一样的,所以继续往下看,assertNotInLayoutOrScroll("Do not setLayoutFrozen in layout or scroll"),这句的作用是判断当前RecyclerView是不是在layout或者scroll,如果是这两种状态,那么就直接抛出异常,也就是说,不能在layout或者scroll的时候去更新adapter。

    然后如果目标状态是解冻,那么就mLayoutFrozen设置为false,然后看mLayoutWasDefered是不是true,也就是有没有layout请求被延迟了,如果有就调用requestLayout(),并将mLayoutWasDefered设置为false。如果目标状态是冻住RecyclerView,那么就生成一个cancelEvent,并且传递给onTouchEvent方法,并将mLayoutFrozen设置为true,最后调用stopScroll()也就是停止RecyclerView的滑动。

    setLayoutFrozen讲完了,回到setAdapter方法源码

        public void setAdapter(@Nullable Adapter adapter) {
            // bail out if layout is frozen
            setLayoutFrozen(false);
            setAdapterInternal(adapter, false, true);
            processDataSetCompletelyChanged(false);
            requestLayout();
        }

    接下来是调用setAdapterInternal(adapter, false, true),源码如下

     private void setAdapterInternal(@Nullable Adapter adapter, boolean compatibleWithPrevious,
                boolean removeAndRecycleViews) {
            if (mAdapter != null) {
                mAdapter.unregisterAdapterDataObserver(mObserver);
                mAdapter.onDetachedFromRecyclerView(this);
            }
            if (!compatibleWithPrevious || removeAndRecycleViews) {
                removeAndRecycleViews();
            }
            mAdapterHelper.reset();
            final Adapter oldAdapter = mAdapter;
            mAdapter = adapter;
            if (adapter != null) {
                adapter.registerAdapterDataObserver(mObserver);
                adapter.onAttachedToRecyclerView(this);
            }
            if (mLayout != null) {
                mLayout.onAdapterChanged(oldAdapter, mAdapter);
            }
            mRecycler.onAdapterChanged(oldAdapter, mAdapter, compatibleWithPrevious);
            mState.mStructureChanged = true;
        }

    主要做了以下事情:

    • 判断mAdapter是不是null,也就是旧的adapter是不是存在,如果存在则注销AdapterDataObserver,并调用onDetachedFromRecyclerView,这个方法的默认实现是空的。
    • 判断两个参数:compatibleWithPreviousremoveAndRecycleViews,这两个参数是在调用setAdapterInternal的时候传进来的,前者表示新的adapter使用的viewholder和itemtype和旧的adapter是一样的,后者表示需不需要将所有已经存在view移除,并回收。在这里compatibleWithPreviousfalseremoveAndRecycleViewstrue,所以我们需要调用removeAndRecycleViews()removeAndRecycleViews()就不讲了,很简单,如果看过前面的文章RecyclerView源码学习笔记(一)构造函数和setLayoutManager方法,就可以轻松阅读源码。
    • 调用mAdapterHelper.reset(),重置AdapterHelper,就是将还未完成的item操作,比如move,add等都删除。
    • 将旧的adapter赋值给oldAdapter,将新的adapter赋值给mAdapter,并注册AdapterDataObserver,调用onAttachedToRecyclerViewonAttachedToRecyclerView默认实现也是空的。
    • 如果LayoutManager不是null,则调用mLayout.onAdapterChanged(oldAdapter, mAdapter),这个方法他们默认实现也是空方法,且SDK中的LayoutManager的子类好像都没有重写这个方法。
    • 调用mRecycler.onAdapterChanged(oldAdapter, mAdapter, compatibleWithPrevious),源码如下
      void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter,
              boolean compatibleWithPrevious) {
          clear();
          getRecycledViewPool().onAdapterChanged(oldAdapter, newAdapter, compatibleWithPrevious);
      }

    先是调用clear方法,该方法做的事情是清空mAttachedScap,将mCachedViews中的view放到pool中,并清空mCachedViews。然后调用RecycledViewPool的onAdapterChanged方法,源码如下

      void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter,
              boolean compatibleWithPrevious) {
          if (oldAdapter != null) {
              detach();
          }
          if (!compatibleWithPrevious && mAttachCount == 0) {
              clear();
          }
          if (newAdapter != null) {
              attach(newAdapter);
          }
      }

    做了以下事情:

    • 如果oldAdapter不为null,则调用detach()方法,这个detach方法内部很简单,只是把mAttachCount减一操作,这个mAttachCount记录了当前有多少个RecyclerView和这个pool建立了关系,因为多个RecyclerView可以共用同一个pool,共用缓存的view,前提是这些view的viewtype要一样。
    • 如果compatibleWithPreviousfalsemAttachCount等于0,则清空pool。
    • 如果newAdapter不为null,则mAttachCount加一

    可以看到这个方法内部还是很简单的。
    回到setAdapterInternal方法,最后将mState.mStructureChanged设置为true
    然后再回到setAdapter方法,接下来会调用processDataSetCompletelyChanged(false),这个方法的注释我也不看出所以然来,就直接上代码:

        void processDataSetCompletelyChanged(boolean dispatchItemsChanged) {
            mDispatchItemsChangedEvent |= dispatchItemsChanged;
            mDataSetHasChangedAfterLayout = true;
            markKnownViewsInvalid();
        }
    • mDispatchItemsChangedEventdispatchItemsChanged进行或运算,把结果赋值给mDispatchItemsChangedEvent,这个值决定在RecyclerView进行measure和layout时候要不要LayoutManager的onItemsChanged(RecyclerView)方法。
    • mDataSetHasChangedAfterLayout设置true
    • 调用markKnownViewsInvalid(),从方法注释来看是将所有view标示为invalid,还是看源码实在
         void markKnownViewsInvalid() {
            final int childCount = mChildHelper.getUnfilteredChildCount();
            for (int i = 0; i < childCount; i++) {
                final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
                if (holder != null && !holder.shouldIgnore()) {
                    holder.addFlags(ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID);
                }
            }
            markItemDecorInsetsDirty();
            mRecycler.markKnownViewsInvalid();
        }
    • 将所有childview(包括不可见的)的viewholder添加ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID标签,标示viewholder已经不可用了
    • 调用markItemDecorInsetsDirty()将所有childview(包括不可见的)的LayoutParam中的mInsetsDirty设置为true,并将mCachedView中的view的LayoutParam中的mInsetsDirty也设置为true,标示ItemDecorInset也需要更新了,而这个ItemDecorInset就是一个矩形,它的值就是我们在重写ItemDecorator的时候需要重写的getItemOffsets方法中设置的。
    • 调用mRecycler.markKnownViewsInvalid(),方法内部是将所有mCachedView中的view的viewholder添加ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID标签,如果新的adapter为null,则直接将所有mCachedView中的view放到pool中,并清空mCachedView。如果有与抓取,就把与抓取的view也清空

    到这里processDataSetCompletelyChanged方法的源码就看完了,接下来回到setAdapter方法,直接调用了requestLayout来进行重新布局。

    setAdapter方法就看完了,主要工作还是清理工作另外就是启动重启布局计算,好了,下一篇文章就开始看布局的过程

    展开全文
  • █ 【安卓学习之UI学习】Dialog的使用总结3 - setAdapter和setItems区别 █ 系列文章目录 提示:这里是收集了和Dialog有关的文章 【android学习开源项目之BasePopup】BasePopup(PopupWindow)进行二次封装 ...

    █ 【安卓学习之UI学习】Dialog的使用总结3 - setAdapter和setItems区别


    █ 系列文章目录

    提示:这里是收集了和Dialog有关的文章

    █ 文章目录


    █ 读前说明

    • 本文通过学习别人写demo,学习一些课件,参考一些博客,’学习相关知识,如果涉及侵权请告知
    • 本文只简单罗列相关的代码实现过程
    • 涉及到的逻辑以及说明也只是简单介绍,主要当做笔记,了解过程而已    

    █ setAdapter使用

    • 效果图
      在这里插入图片描述

    • 布局:android.R.select_dialog_item
      可以看出,通过修改textColorAlertDialogListItem来设置字体颜色
      也可以自定义layout(修改颜色、字体、居中等)

    <TextView xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@android:id/text1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:minHeight="?android:attr/listPreferredItemHeight"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:textColor="?android:attr/textColorAlertDialogListItem"
        android:gravity="center_vertical"
        android:paddingStart="14dip"
        android:paddingEnd="15dip"
        android:ellipsize="marquee"
    />
    
    • 代码:
     final ArrayAdapter<String> adapter = new ArrayAdapter<>(context, android.R.layout.select_dialog_item);
     adapter.add("数学");
     adapter.add("语文");
     adapter.add("英文");
    
    AlertDialog.Builder builder = new AlertDialog.Builder(activity);
    builder.setTitle("请选择你喜欢的课程")
            .setPositiveButton(android.R.string.cancel, null)// 取消
            .setAdapter(arrayAdapter, new DialogInterface.OnClickListener() {
    		    @Override
    		    public void onClick(DialogInterface dialog, int which) {
    		        // which = 0 1 2
    		        Toast.makeText(activity, adapter.getItem(which), Toast.LENGTH_SHORT).show();
    		    }
    		});
    builder.create().show();
    

    █ setItems使用

    • 效果图
      在这里插入图片描述

    • 布局:android.R.layout.select_dialog_singlechoice
      可以看出,通过修改textColorAlertDialogListItem来设置字体颜色

    <CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@android:id/text1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:minHeight="?android:attr/listPreferredItemHeight"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:textColor="?android:attr/textColorAlertDialogListItem"
        android:gravity="center_vertical"
        android:paddingStart="12dip"
        android:paddingEnd="7dip"
        android:checkMark="?android:attr/listChoiceIndicatorSingle"
        android:ellipsize="marquee"
    />
    
    
    • 代码:
    final String[] fileLabels = {"数学","语文","英文","体育"};
    
    AlertDialog.Builder builder = new AlertDialog.Builder(activity);
    builder.setTitle("请选择你喜欢的课程");
    builder.setItems(fileLabels, new OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
            // which = 0 1 2 3
            Toast.makeText(activity, fileLabels[which], Toast.LENGTH_SHORT).show();
            // Snackbar.make(view, fileLabels[which], Snackbar.LENGTH_LONG).setAction("Action", null).show();
        }
    });
    builder.create().show();
    

    █ setAdapter和setItems区别

    • builder.setAdapter
    public class AlertDialog extends Dialog implements DialogInterface {
        private AlertController mAlert;
        public static class Builder {
            private final AlertController.AlertParams P;
            /**
             * Set a list of items, which are supplied by the given {@link ListAdapter}, to be
             * displayed in the dialog as the content, you will be notified of the
             * selected item via the supplied listener.
             * 设置由给定的 {@link ListAdapter} 提供的项目列表,作为内容显示在对话框中,您将通过提供的侦听器收到所选项目的通知。
             * 
             * @param adapter The {@link ListAdapter} to supply the list of items
             * 		 adapter 提供项目列表
             * @param listener The listener that will be called when an item is clicked.
             * 		 listener 单击项目时将调用的侦听器。
             * @return This Builder object to allow for chaining of calls to set methods
             * 		   这个 Builder 对象允许链接调用设置方法
             */
            public Builder setAdapter(final ListAdapter adapter, final OnClickListener listener) {
                P.mAdapter = adapter;
                P.mOnClickListener = listener;
                return this;
            }
        }
    }
    
    • builder.setItems
    public class AlertDialog extends Dialog implements DialogInterface {
        private AlertController mAlert;
        public static class Builder {
            private final AlertController.AlertParams P;
            /**
             * Set a list of items to be displayed in the dialog as the content, you will be notified of the
             * selected item via the supplied listener.
             * 设置要在对话框中显示的项目列表作为内容,您将通过提供的侦听器收到所选项目的通知。
             * @return This Builder object to allow for chaining of calls to set methods
             * 		   这个 Builder 对象允许链接调用设置方法
             */
            public Builder setItems(CharSequence[] items, final OnClickListener listener) {
                P.mItems = items;
                P.mOnClickListener = listener;
                return this;
            }
        }
    }
    
    • mAdapter 、mItems 使用地方:
      public static class AlertParams {
            public final Context mContext;
            public final LayoutInflater mInflater;
            
            public CharSequence[] mItems;
            public ListAdapter mAdapter;
            public boolean[] mCheckedItems;
            public boolean mIsMultiChoice;
            public boolean mIsSingleChoice;
            public DialogInterface.OnMultiChoiceClickListener mOnCheckboxClickListener;
            public DialogInterface.OnClickListener mOnClickListener;
            
            public void apply(AlertController dialog) {
            	。。。。。。
                // For a list, the client can either supply an array of items or an
                // adapter or a cursor
                if ((mItems != null) || (mCursor != null) || (mAdapter != null)) {
                    createListView(dialog);
                }
                。。。。。。
            }
            private void createListView(final AlertController dialog) {
                final RecycleListView listView = (RecycleListView)
                        mInflater.inflate(dialog.mListLayout, null);
                ListAdapter adapter;
                
                if (mIsMultiChoice) {// 多选框
                    。。。。。。
                } else {// 单选框 优先使用 mCursor数据,其次使用mAdapter数据,最后再使用mItems
                    int layout = mIsSingleChoice 
                            ? dialog.mSingleChoiceItemLayout : dialog.mListItemLayout;
                    if (mCursor == null) {
                        adapter = (mAdapter != null) ? mAdapter
                                : new ArrayAdapter<CharSequence>(mContext, layout, R.id.text1, mItems);
                    } else {
                        adapter = new SimpleCursorAdapter(mContext, layout, 
                                mCursor, new String[]{mLabelColumn}, new int[]{R.id.text1});
                    }
                }
                
                dialog.mAdapter = adapter;
                dialog.mCheckedItem = mCheckedItem;
                
                if (mOnClickListener != null) {
                    listView.setOnItemClickListener(new OnItemClickListener() {
                        public void onItemClick(AdapterView parent, View v, int position, long id) {
                            mOnClickListener.onClick(dialog.mDialogInterface, position);
                            if (!mIsSingleChoice) {// 单选框,直接关闭对话框
                                dialog.mDialogInterface.dismiss();
                            }
                        }
                    });
                } else if (mOnCheckboxClickListener != null) {
                    listView.setOnItemClickListener(new OnItemClickListener() {
                        public void onItemClick(AdapterView parent, View v, int position, long id) {
                            if (mCheckedItems != null) {
                                mCheckedItems[position] = listView.isItemChecked(position);
                            }
                            mOnCheckboxClickListener.onClick(
                                    dialog.mDialogInterface, position, listView.isItemChecked(position));
                        }
                    });
                }
                
                if (mIsSingleChoice) {
                    listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
                } else if (mIsMultiChoice) {
                    listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
                }
                listView.mRecycleOnMeasure = mRecycleOnMeasure;
                dialog.mListView = listView;
            }
        }
    
    
    

    █ 其他


    █ 相关资料

    提示:这里是参考的相关文章

    1. 2013-01-23 android源码浅析–AlertController_傲慢的上校的专栏-CSDN博客

    █ 免责声明

    博主分享的所有文章内容,部分参考网上教程,引用大神高论,部分亲身实践,记下笔录,内容可能存在诸多不实之处,还望海涵,本内容仅供学习研究使用,切勿用于商业用途,若您是部分内容的作者,不喜欢此内容被分享出来,可联系博主说明相关情况通知删除,感谢您的理解与支持!

    转载请注明出处:
    https://blog.csdn.net/ljb568838953/article/details/120077358

    展开全文
  • *也就是说,SimpleCursorAdapter 允许绑定一个 Cursor 的 columns 到 ListView 上,并使用自定义的 layout 显示 List中的每个项目。 *可以使用 SimpleCursorAdapter 作为中间桥梁,将从 sqlite 数据库中查询出来...

    最简单的界面布局,就只一个ListView

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"

        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
        <ListView
            android:id="@+id/test_lv"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" 
            android:divider="@null">
        </ListView>

    </RelativeLayout>

    界面Activity代码,把几种写在一起的(为了省事):

    public class MainActivity extends Activity {
    /**ListView对象*/
      private ListView listView;
    @Override
    public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    /**实例化listView */
    listView=(ListView) findViewById(R.id.test_lv);
    /**设置适配器,填充数据 (以下有四种方式,任选其一都可以)*/
    setAdapterMothodOne();
    setAdapterMothodTwo();
    setAdapterMothodThree();
    setAdapterMothodFour();
    }
         /**第一种方式,用ArrayAdapter*/
    private void setAdapterMothodOne(){
    listView.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, getData()));//这里使用的android自带的android.R.layout.simple_list_item_1
    }
    /**第二种方式,用SimpleAdapter*/
    @SuppressWarnings("unchecked")
    private void setAdapterMothodTwo(){
    /**
    * SimpleAdapter并不Simple,可以用SimpleAdapter做出很多样式的ListView
    * 参数介绍:context  SimpleAdapter关联的View的运行环境
    * data    一个Map组成的List。在列表中的每个条目对应列表中的一行,每一个map中应该包含所有在from参数中指定的键
    * resource    一个定义列表项的布局文件的资源ID。布局文件将至少应包含那些在to中定义了的ID
    * from          一个将被添加到Map映射上的键名
    * to     将绑定数据的视图的ID,跟from参数对应,这些应该全是TextView
    */
    listView.setAdapter(new SimpleAdapter(this, (List<? extends Map<String, ?>>) getMapData(), R.layout.list_items,
    new String[] { "title", "content", "img" }, new int[] { R.id.title, R.id.content, R.id.image }));
    }
    /**第三种方式,用SimpleCursorAdapter*/
    @SuppressWarnings("deprecation")
    private void setAdapterMothodThree(){
    /**
    *SimpleCurosrAdapter 是一个将 Cursor 中的 columns 与在 XML 文件中定义的 TextViews 或 ImageViews 进行匹配的简易 adapter。你可以指定选择 Cursor 中的哪些 columns、用哪些 views 来显示这些 columns 、以及指定定义这些 views 的 xml 文件。 
    *也就是说,SimpleCursorAdapter 允许绑定一个 Cursor 的 columns 到 ListView 上,并使用自定义的 layout 显示 List中的每个项目。
    *可以使用 SimpleCursorAdapter 作为中间桥梁,将从 sqlite 数据库中查询出来的数据直接显示到 ListView 中。
    */
    listView.setAdapter(new SimpleCursorAdapter(this, android.R.layout.simple_list_item_2, getCursorData(),
    new String[] { People.NAME, People.NUMBER }, new int[] { android.R.id.text1, android.R.id.text2 }));
    }
    /**第四种方式,自定义Adapter,这种方式应用最多最广泛*/
    private void setAdapterMothodFour(){
    listView.setAdapter(new MyAdapter(MainActivity.this, (ArrayList)getData()));
    }
    /* 单个数据集合 */
    private List<String> getData() {
    List<String> list = new ArrayList<String>();
    for (int i = 0; i < 5; i++) {
    list.add("my_test_" + i);
    }
    return list;
    }


    /* 多个数据集合 */
    private List<Map> getMapData() {
    List<Map> list = new ArrayList<Map>();
    for (int i = 0; i < 5; i++) {
    Map<String, Object> map = new HashMap<String, Object>();
    map.put("title", "title_" + i);
    map.put("content", "content_" + i + ".............................");
    map.put("img", R.drawable.ic_launcher);
    list.add(map);
    }
    return list;
    }


    /* 动态数据 */
    private Cursor getCursorData() {
    Cursor cursor = getContentResolver().query(People.CONTENT_URI, null, null, null, null);
    startManagingCursor(cursor);
    return cursor;
    }
    }

    又是个最简单的适配器Adapter

    public class MyAdapter extends BaseAdapter {
    private Context context;
    private ArrayList<String>data;
    public MyAdapter(Context context,ArrayList<String> data){
    this.context=context;
    this.data=data;
    }
    @Override
    public int getCount() {
    // TODO Auto-generated method stub
    return data.size();
    }


    @Override
    public Object getItem(int position) {
    // TODO Auto-generated method stub
    return data.get(position);
    }


    @Override
    public long getItemId(int position) {
    // TODO Auto-generated method stub
    return position;
    }


    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
    TextView tv=new TextView(context);//这里只是一个非常简单的例子,我们开发中应该考虑到优化的
    tv.setText(data.get(position));
    return tv;
    }


    }

    展开全文
  • 另外系统为什么找不到setListAdapter()方法,还有activity可以继承自AppCompatActivity吗这是activity代码package com.example.application;import android.app.Activity;import android.app.ListActivity;import ...
  • 在android中经常使用到listview或者其他AbsListView,以下用listview加以说明。 对于其listview的内存回收,需要使用如下方式: 在activity的onstart生命状态中执行重建操作: listview.addHeaderView(View); ...
  • AutoCompleteTextView 就是输入账号密码可以自动补全的... 网上看教程之后 就是setAdapter 不进去 auto.setAdapter(adapter); Log.d(TAG, "setAdapter"); auto.setDropDownHeight(1500); auto.set...
  • 解决ViewPager展示Fragment时重新设置setAdapter不会重置Fragment的bug 我遇到的问题: 创建了新的fragment也设置了setAdapter但是结果还是出现旧的fragment 解决方法如下: 1在你的PageAdapter中增加下面一个方法...
  • ListView想要添加headerview的话,就要通过addHeaderView这个方法,然后想要为ListView设置数据的话,就要调用setAdapter方法了。但是,在调用addHeaderView和setAdapter的顺序上,有时候会报这样的一个异常:java....
  • ListView.setAdapter流程以及缓存机制

    千次阅读 2019-03-24 17:23:26
    1. ListView数据显示的流程分析 ...实现机制到底是怎么样的呢,那么我们来看看listView的setAdapter方法是怎么实现的吧: ListView.java public void setAdapter(ListAdapter adapter) { .... requestLayo...
  •  后来查了下两者的区别:其实2个都可以更新,notifyDataSetChanged与setAdapter执行后都会到getView方法中,不同在于调用setAdapter的话view是空的需要重新创建,而前者传的是之前的view实体;另一个区别setAdapter...
  • 但其中一个fragmentActivity1中使用ListVIew的setAdapter()方法时,总是报NullPointerException错误。 一整天都没有解决,晚上看国外的论坛上,看到对于ListView的深入讲解,才忽然理解了问题所在。 对于...
  • Recycleview之setAdapter源码分析

    千次阅读 2016-10-25 15:46:14
    recycleview作为取代listview的新控件,使用率越来越高了,在这之前我都是简单的使用recycleview没有仔细的研究过它,所以我决定从它的源码入手看看它是如何实现的. recycleview的设计非常的解耦,采用设计模式中的...
  • java.lang.IllegalStateException: Cannot set menu creator, setAdapter has already been called. at com.yanzhenjie.recyclerview.SwipeRecyclerView.checkAdapterExist(SwipeRecyclerView.java:245) at ...
  • private PullToRefreshExpandableListView lv; lv = (PullToRefreshExpandableListView) findViewById(R.id.lv); lv.setAdapter(new MyAdapter...今天使用PullToRefreshExpandableListView做带下拉的二级列表,开...
  • 用过ListView和RecycleView的人都知道不管当前...结果并不是我想的那么简单,重复setAdapter并没什么卵用,Fragment的状态还是上一次所看到的样子,并没有重新初始化,Fragment的几什么周期方法一个都没有运行…实在无语...
  • Android:三种Adapter的使用方法

    千次阅读 2021-02-28 23:50:30
    三种Adapter的使用方法1、simpleAdapter使用1).adapter方法说明2、baseAdapter使用1).adapter方法说明2).创建adapter并继承BaseAdapter3).创建一个 listview xml 文件3、arrayAdapter使用1). 直接引用String-...
  • 使用setAdapter时我们都是按照listView的优化套路来的如下,在实际应用比较容易出空指针bug,究竟是谁为空了,两个地方,传入的集合和引入的layout布局只有这两个量是外来量,但这里有一个非常有趣的地方,就是它...
  • private PullToRefreshExpandableListView lv; lv = (PullToRefreshExpandableListView) findViewById(R.id.lv); lv.setAdapter(new ...今天使用PullToRefreshExpandableListView做带下拉的二级列表,开始用的Exp
  • 给ListView(RecyclerView)设置Adapter算是Android开发中最常用的操作之一。...mListView.setAdapter(mAdapter); 在ListView发生变化之后,调用 mAdapter.notifyDataSetChanged();通知列表进行改变。 这
  • 先贴出异常日志 03-06 23:39:51.101: E/AndroidRuntime(22049): FATAL EXCEPTION: main ...总结:在使用findViewById()方法时,一定要注意你所使用的控件所在的上下文Context,这样可以避免很多麻烦。
  • ListView想要添加headerview的话,就要通过addHeaderView这个方法,然后想要为ListView设置数据的话,就要调用setAdapter方法了。但是,在调用addHeaderView和setAdapter的顺序上,有时会爆出java.lang....
  • method 'void android.widget.Spinner.setAdapter(android.widget.SpinnerAdapter)' on a null object reference
  • 使用Android GridView时出现如下报错: **FATAL EXCEPTION: main Process: aspectj.lhxtest2, PID: 25329 java.lang.RuntimeException: Unable to start activity ComponentInfo{aspectj.lhxtest2/aspectj....
  • private boolean pageScrolled(int xpos) {}方法会调用onPageScrolled(currentPage, pageOffset, offsetPixels)方法,在onPageScrolled方法里又调用了dispatchOnPageScrolled方法,在这个方法中会判断是否是否设置...
  • 1. 图片轮播使用Gallery,用法很简单 GalleryAdapter adapter = new GalleryAdapter(this, mIds); mGallery.setAdapter(adapter); GalleryAdapter是自定义适配器 public class GalleryAdapter extends ...
  • 用法上和 ListView 很像。如图: 我们看到, PagerAdapter 持有数据集 DataSetObservable ,同时包含一些回调。 setAdapter() 那么很自然的,我们从 ViewPager 的 setAdapter 开始分析把。 ...
  • 主要为大家详细介绍了Android控件ListView的使用方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 52,149
精华内容 20,859
关键字:

setadapter用法