Android View系统源码分析(十)—— View.setVisibility(int visibility)

View.setVisibility

该函数用于改变视图的可视状态,可视状态包括GONE、VISIBLE、INVISIBLE三种。该函数内部很简单,首先调用setFlags(),然后调用mBGDrawable.setVisible()函数来改变视图背景图的显示状态。

View.setVisibilityint-visibility

点我下载VSDX

View.setVisibility(int visibility)

 /**
     * Set the enabled state of this view.
     * 设置视图的激活状态
     * @param visibility One of {@link #VISIBLE}, {@link #INVISIBLE}, or {@link #GONE}.
     * @attr ref android.R.styleable#View_visibility
     */
    @RemotableViewMethod
    public void setVisibility(int visibility) {
 
        //设置visibility属性位的标记。
        setFlags(visibility, VISIBILITY_MASK);
 
        //如果当前视图的背景不为空,调用当前背景drawable对象的.setVisible
        if (mBGDrawable != null) mBGDrawable.setVisible(visibility == VISIBLE, false);
    }
 
 
 
该函数被广泛使用,例如 View.setEnable()、View.setClickable()等很多函数都调用到该函数。
在View中使用mViewFlags和mPrivateFlags变量保存大多数的属性:
– mViewFlags:保存和视图状态相关的属性。
– mPrivateFlags保存和内部逻辑相关的属性
View.setFlags(int flags , int mask)


/**
     * Set flags controlling behavior of this view.
     * 设置标记来管理视图的状态
     * @param flags Constant indicating the value which should be set
     * @param mask Constant indicating the bit range that should be changed
     */
 
    void setFlags(int flags , int mask) {
 
        //保存当前视图状态为old
        int old = mViewFlags;
 
        //更新视图状态为将要更改后的属性。
        mViewFlags = (mViewFlags & ~mask) | (flags & mask);
 
 
        //获取其更改的属性位
        int changed = mViewFlags ^ old;
 
        //如果更改属性位为0,表示其并没有做任何的视图改变,直接return。
        if (changed == 0) {
            return;
        }
 
        //记录当前视图的逻辑属性。
        int privateFlags = mPrivateFlags;
 
        /* Check if the FOCUSABLE bit has changed */
        //判断如果 FOCUSABLE位 有变更
 
        //如果 视图变更位changed中的FOCUSABLE位区域有变更,并且记录的当前视图的逻辑位privateFlags表示该视图是有边界的(存在视图大小的)。
        if (((changed & FOCUSABLE_MASK) != 0) &&
                ((privateFlags & HAS_BOUNDS) !=0)) {
 
            //如果当前视图位的FOCUSABLE属性位为有获取焦点的能力,并且当前逻辑privateFlags表示该视图已经获取了焦点
            if (((old & FOCUSABLE_MASK) == FOCUSABLE)
                    && ((privateFlags & FOCUSED) != 0)) {
 
                //如果不是具备长焦点获取能力的话,就放弃当前视图的焦点。
                /* Give up focus if we are no longer focusable */
 
                //调用View.clearFocus(),清除焦点。
                clearFocus();
 
                //如果当前视图的FOCUSABLE属性位为没有获取焦点的能力,并且当前的逻辑标记位无焦点。
            } else if (((old & FOCUSABLE_MASK) == NOT_FOCUSABLE)
                    && ((privateFlags & FOCUSED) == 0)) {
                /*
                 * Tell the view system that we are now available to take focus
                 * if no one else already has it.
                 * 通知视图系统,如果现在该视图没有焦点的话我们要让其拥有焦点。
                 */
 
                //如果视图的父视图不为空,通知其父视图,有新的子视图可以获得焦点。这里仅仅是通知其父视图,至于父视图是否要把焦点分配给该视图则不一定。
                if (mParent != null) mParent.focusableViewAvailable(this);
            }
        }
 
        //如果设置标记的flags的VISIBILITY位为VISIBLE
        if ((flags & VISIBILITY_MASK) == VISIBLE) {
            //如果VISIBILITY位有变化
            if ((changed & VISIBILITY_MASK) != 0) {
                /*
                 * If this view is becoming visible, set the DRAWN flag so that
                 * the next invalidate() will not be skipped.
                 * 如果这个视图将要成为可视的,设置DRAWN的标记,以至于在下次重绘时将不跳过其绘制。
                 */
 
                //逻辑一标记mPrivateFlags添加 DRAWN标记。
                mPrivateFlags |= DRAWN;
 
                //该函数将给mAttachInfo对象中的mRecomputeGloabalAttributes变量赋值为true。该变量的含义实际上是指“需要从on更新计算View树中视图的位置”,因为该视图显示查出来后一般会导致其他视图在界面上的位置发生变化,因此View系统需要重新计算位置。
                needGlobalAttributesUpdate(true);
 
                // a view becoming visible is worth notifying the parent
                // about in case nothing has focus.  even if this specific view
                // isn't focusable, it may contain something that is, so let
                // the root view try to give this focus if nothing else does.
 
                //如果有父视图并且该视图为合法视图(也就是有大小的视图)
                if ((mParent != null) && (mBottom > mTop) && (mRight > mLeft)) {
 
                    //通知其父视图有了新的子视图。添加的新视图不管怎样都需要将其申请一次获取焦点,即使其没有获取焦点的能力。
                    mParent.focusableViewAvailable(this);
                }
            }
        }
 
        /* Check if the GONE bit has changed */
        //检查如果是GONE属性位发生了变化
 
        //如果视图的属性要设置为GONE
        if ((changed & GONE) != 0) {
 
            //需要全局属性更新,因为GONE属性设置其视图不见了,其他视图的位置也会受到影响。
            needGlobalAttributesUpdate(false);
 
            //申请重新布局
            requestLayout();
 
            //重新绘制视图。
            invalidate();
 
            //如果更改后的视图属性的VISIBILITY属性位为GONE
            if (((mViewFlags & VISIBILITY_MASK) == GONE)) {
 
                //如果当前视图有焦点的话,就将其焦点清除
                if (hasFocus()) clearFocus();
 
                //释放视图所使用的缓存,当然并不是所有的视图都有缓存。
                destroyDrawingCache();
            }
 
            //如果mAttachInfo对象不为空
            if (mAttachInfo != null) {
 
                //将对象中的mVisibilityChanged设置为true。
                mAttachInfo.mViewVisibilityChanged = true;
            }
        }
 
        /* Check if the VISIBLE bit has changed */
        //检查如果VISIBLE的属性位发生了变化
 
        //如果视图将要设置为INVISIBLE了
        if ((changed & INVISIBLE) != 0) {
 
            //不需要全局属性更新
            needGlobalAttributesUpdate(false);
 
            //重新绘制制图
            invalidate();
 
            //如果作用后的视图属性的VISIBILITY位为INVISIBLE 并且 当前视图拥有焦点
            if (((mViewFlags & VISIBILITY_MASK) == INVISIBLE) && hasFocus()) {
                // root view becoming invisible shouldn't clear focus
 
                //如果当前视图不是根视图的话
                if (getRootView() != this) {
                    //清除焦点
                    clearFocus();
                }
            }
 
            if (mAttachInfo != null) {
                //更改该视图的标记对象的mViewVisibilityChanged为true。
                mAttachInfo.mViewVisibilityChanged = true;
            }
        }
 
 
        //如果更改的属性位的VISIBILITY属性位有改变
        if ((changed & VISIBILITY_MASK) != 0) {
 
            //该函数会回调onVisibilityChanged(),应用程序可以重载该函数以便执行其他操作。
            dispatchVisibilityChanged(this, (flags & VISIBILITY_MASK));
        }
 
 
        //对视图缓存以及绘制标记的处理。
        if ((changed & WILL_NOT_CACHE_DRAWING) != 0) {
            destroyDrawingCache();
        }
 
        if ((changed & DRAWING_CACHE_ENABLED) != 0) {
            destroyDrawingCache();
            mPrivateFlags &= ~DRAWING_CACHE_VALID;
        }
 
        if ((changed & DRAWING_CACHE_QUALITY_MASK) != 0) {
            destroyDrawingCache();
            mPrivateFlags &= ~DRAWING_CACHE_VALID;
        }
 
 
        //如果DRAW_MASK标志位有改变。
        if ((changed & DRAW_MASK) != 0) {
 
            //如果视图将设置为 WILL_NOT_DRAW属性
            if ((mViewFlags & WILL_NOT_DRAW) != 0) {
 
                //如果视图背景不为空
                if (mBGDrawable != null) {
 
                    //去掉SKIP_DRAW的标志位
                    mPrivateFlags &= ~SKIP_DRAW;
 
                    //添加ONLY_DRAWS_BACKGROUND标志位
                    mPrivateFlags |= ONLY_DRAWS_BACKGROUND;
                } else {
 
                    //如果视图背景为空,添加跳过绘制标志位
                    mPrivateFlags |= SKIP_DRAW;
                }
            } else {
 
                //如果DRAW_MASK标志位没有改变,清除SKIP_DRAW的标志位
                mPrivateFlags &= ~SKIP_DRAW;
            }
 
            //重新申请布局,重新绘制视图
            requestLayout();
            invalidate();
        }
 
        //如果改变的标志位的 KEEP_SCREEN_ON 属性位有改变
        if ((changed & KEEP_SCREEN_ON) != 0) {
 
            //如果父视图不为空
            if (mParent != null) {
 
                //父视图重新计算当前视图的属性。
                mParent.recomputeViewAttributes(this);
            }
        }
    }
 
 
ViewParent.focusableViewAvailable(View v)
    /**
     * Tells the parent that a new focusable view has become available. This is
     * to handle transitions from the case where there are no focusable views to
     * the case where the first focusable view appears.
     * 通知父视图一个新的拥有焦点能力视图将会被添加。该方法处理该视图在第一次出现时(这个时候是不管其是否有获取焦点的能力)来获取焦点(也就是第一次添加视图时该视图都需要获取一次焦点)。
     * 
     * @param v The view that has become newly focusable
     *  
     */
    public void focusableViewAvailable(View v);
 
 
 
View.destroyDrawingCache()


    /**
     * <p>Frees the resources used by the drawing cache. If you call
     * {@link #buildDrawingCache()} manually without calling
     * {@link #setDrawingCacheEnabled(boolean) setDrawingCacheEnabled(true)}, you
     * should cleanup the cache with this method afterwards.</p>
     * 释放正在使用的绘制缓存资源。如果调用了 buildDrawingCache方法,
     * 或者手工调用了setDrawingCacheEnabled(boolean) 
     * setDrawingCacheEnabled(true)方法之后必须要清空缓存。
     *
     * @see #setDrawingCacheEnabled(boolean)
     * @see #buildDrawingCache()
     * @see #getDrawingCache()
     */
    public void destroyDrawingCache() {
 
        //如果绘制缓存不为空
        if (mDrawingCache != null) {
 
            //获取缓存中的bitmap数据引用
            final Bitmap bitmap = mDrawingCache.get();
 
            //如果bitmap不为空,就执行回收bitmap对象
            if (bitmap != null) bitmap.recycle();
 
            //设置绘制缓存为空
            mDrawingCache = null;
        }
 
        //缩略图缓存不为空
        if (mUnscaledDrawingCache != null) {
 
            //获取缩略图缓存的bitmap数据的引用
            final Bitmap bitmap = mUnscaledDrawingCache.get();
 
            //如果bitmap不为空,就执行回收bitmap对象
            if (bitmap != null) bitmap.recycle();
 
            //设置缩略图绘制缓存对象为空
            mUnscaledDrawingCache = null;
        }
    }
 
 
View.clearFocus()
 /**
     * Called when this view wants to give up focus. This will cause
     * 
     * {@link #onFocusChanged} to be called.
     * 如果视图放弃了焦点时调用的方法。该方法将回导致View.onFocusChanged方法的调用
     */
    public void clearFocus() {
        if (DBG) {
            System.out.println(this + " clearFocus()");
        }
 
        //如果当前视图的逻辑属性位为有焦点的
        if ((mPrivateFlags & FOCUSED) != 0) {
 
            //清空逻辑属性位的焦点属性。
            mPrivateFlags &= ~FOCUSED;
 
            //如果当前视图有父视图
            if (mParent != null) {
 
                //通知其父视图清空其子视图的焦点
                mParent.clearChildFocus(this);
            }
 
 
            //调用onFocusChanged方法
            onFocusChanged(false, 0, null);
 
            //刷新绘制状态。
            refreshDrawableState();
        }
    }
 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是一个简单的Android仿淘宝购物车的ListView实例: 1. 布局文件: ``` <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <ListView android:id="@+id/list_view" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" /> <TextView android:id="@+id/tv_empty_cart" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:text="购物车空空如也~" android:textSize="20sp" android:visibility="gone" /> </LinearLayout> ``` 2. 条目布局文件item_cart.xml: ``` <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content"> <ImageView android:id="@+id/iv_cart_item" android:layout_width="80dp" android:layout_height="80dp" android:layout_margin="10dp" android:src="@mipmap/ic_launcher" /> <TextView android:id="@+id/tv_cart_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentTop="true" android:layout_marginTop="10dp" android:layout_toEndOf="@id/iv_cart_item" android:text="商品名称" android:textColor="#000000" android:textSize="16sp" /> <TextView android:id="@+id/tv_cart_price" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/tv_cart_name" android:layout_marginTop="10dp" android:layout_toEndOf="@id/iv_cart_item" android:text="¥0.00" android:textColor="#ff0000" android:textSize="14sp" /> <TextView android:id="@+id/tv_cart_count" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignBottom="@id/iv_cart_item" android:layout_marginTop="10dp" android:layout_toEndOf="@id/iv_cart_item" android:text="数量:0" android:textColor="#000000" android:textSize="14sp" /> <Button android:id="@+id/btn_cart_delete" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentEnd="true" android:layout_centerVertical="true" android:layout_marginEnd="10dp" android:text="删除" /> </RelativeLayout> ``` 3. ListView的Adapter: ``` public class CartAdapter extends BaseAdapter { private Context mContext; private List<CartItem> mCartItemList; public CartAdapter(Context context, List<CartItem> cartItemList) { mContext = context; mCartItemList = cartItemList; } @Override public int getCount() { return mCartItemList.size(); } @Override public Object getItem(int position) { return mCartItemList.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(final int position, View convertView, ViewGroup parent) { ViewHolder viewHolder; if (convertView == null) { convertView = LayoutInflater.from(mContext).inflate(R.layout.item_cart, parent, false); viewHolder = new ViewHolder(); viewHolder.ivCartItem = convertView.findViewById(R.id.iv_cart_item); viewHolder.tvCartName = convertView.findViewById(R.id.tv_cart_name); viewHolder.tvCartPrice = convertView.findViewById(R.id.tv_cart_price); viewHolder.tvCartCount = convertView.findViewById(R.id.tv_cart_count); viewHolder.btnDelete = convertView.findViewById(R.id.btn_cart_delete); convertView.setTag(viewHolder); } else { viewHolder = (ViewHolder) convertView.getTag(); } final CartItem cartItem = mCartItemList.get(position); viewHolder.ivCartItem.setImageResource(cartItem.getImageId()); viewHolder.tvCartName.setText(cartItem.getName()); viewHolder.tvCartPrice.setText(String.format("¥%.2f", cartItem.getPrice())); viewHolder.tvCartCount.setText(String.format("数量:%d", cartItem.getCount())); viewHolder.btnDelete.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mCartItemList.remove(position); notifyDataSetChanged(); // 更新本地存储的购物车数据 updateCartData(mCartItemList); } }); return convertView; } private static class ViewHolder { ImageView ivCartItem; TextView tvCartName; TextView tvCartPrice; TextView tvCartCount; Button btnDelete; } // 更新本地存储的购物车数据 private void updateCartData(List<CartItem> cartItemList) { // TODO } } ``` 4. 在Activity中初始化ListView和Adapter,并绑定数据: ``` public class CartActivity extends AppCompatActivity { private ListView mListView; private TextView mTvEmptyCart; private List<CartItem> mCartItemList; private CartAdapter mCartAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_cart); mListView = findViewById(R.id.list_view); mTvEmptyCart = findViewById(R.id.tv_empty_cart); // 从本地存储中读取购物车数据 mCartItemList = readCartData(); if (mCartItemList.size() > 0) { mCartAdapter = new CartAdapter(this, mCartItemList); mListView.setAdapter(mCartAdapter); } else { mListView.setVisibility(View.GONE); mTvEmptyCart.setVisibility(View.VISIBLE); } } // 从本地存储中读取购物车数据 private List<CartItem> readCartData() { // TODO return new ArrayList<>(); } } ``` 希望这个实例能够帮到您!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值