• Android仿QQ个人界面,抽屉布局 NavigationView使用详解


    NavigationView,导航视图,比如QQ的侧滑菜单,如下图,分为head和menu上下两部分head【图片,昵称,uid】menu【下面的菜单】

    添加implementation 'com.android.support:design:29.+'

    圆形头像CircleImageView或者是

    //实现图片圆形化

    compile 'de.hdodenhof:circleimageview:2.1.0'2.

    1. /**
    2. 圆形头像
    3. * */
    4. public class CircleImageView extends ImageView {
    5. private static final ScaleType SCALE_TYPE = ScaleType.CENTER_CROP;
    6. private static final Bitmap.Config BITMAP_CONFIG = Bitmap.Config.ARGB_8888;
    7. private static final int COLORDRAWABLE_DIMENSION = 2;
    8. private static final int DEFAULT_BORDER_WIDTH = 0;
    9. private static final int DEFAULT_BORDER_COLOR = Color.BLACK;
    10. private static final int DEFAULT_FILL_COLOR = Color.TRANSPARENT;
    11. private static final boolean DEFAULT_BORDER_OVERLAY = false;
    12. private final RectF mDrawableRect = new RectF();
    13. private final RectF mBorderRect = new RectF();
    14. private final Matrix mShaderMatrix = new Matrix();
    15. private final Paint mBitmapPaint = new Paint();
    16. private final Paint mBorderPaint = new Paint();
    17. private final Paint mFillPaint = new Paint();
    18. private int mBorderColor = DEFAULT_BORDER_COLOR;
    19. private int mBorderWidth = DEFAULT_BORDER_WIDTH;
    20. private int mFillColor = DEFAULT_FILL_COLOR;
    21. private Bitmap mBitmap;
    22. private BitmapShader mBitmapShader;
    23. private int mBitmapWidth;
    24. private int mBitmapHeight;
    25. private float mDrawableRadius;
    26. private float mBorderRadius;
    27. private ColorFilter mColorFilter;
    28. private boolean mReady;
    29. private boolean mSetupPending;
    30. private boolean mBorderOverlay;
    31. public CircleImageView(Context context) {
    32. super(context);
    33. init();
    34. }
    35. public CircleImageView(Context context, AttributeSet attrs) {
    36. this(context, attrs, 0);
    37. }
    38. public CircleImageView(Context context, AttributeSet attrs, int defStyle) {
    39. super(context, attrs, defStyle);
    40. TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CircleImageView, defStyle, 0);
    41. mBorderWidth = a.getDimensionPixelSize(R.styleable.CircleImageView_civ_border_width, DEFAULT_BORDER_WIDTH);
    42. mBorderColor = a.getColor(R.styleable.CircleImageView_civ_border_color, DEFAULT_BORDER_COLOR);
    43. mBorderOverlay = a.getBoolean(R.styleable.CircleImageView_civ_border_overlay, DEFAULT_BORDER_OVERLAY);
    44. mFillColor = a.getColor(R.styleable.CircleImageView_civ_fill_color, DEFAULT_FILL_COLOR);
    45. a.recycle();
    46. init();
    47. }
    48. private void init() {
    49. super.setScaleType(SCALE_TYPE);
    50. mReady = true;
    51. if (mSetupPending) {
    52. setup();
    53. mSetupPending = false;
    54. }
    55. }
    56. @Override
    57. public ScaleType getScaleType() {
    58. return SCALE_TYPE;
    59. }
    60. @Override
    61. public void setScaleType(ScaleType scaleType) {
    62. if (scaleType != SCALE_TYPE) {
    63. throw new IllegalArgumentException(String.format("ScaleType %s not supported.", scaleType));
    64. }
    65. }
    66. @Override
    67. public void setAdjustViewBounds(boolean adjustViewBounds) {
    68. if (adjustViewBounds) {
    69. throw new IllegalArgumentException("adjustViewBounds not supported.");
    70. }
    71. }
    72. @Override
    73. protected void onDraw(Canvas canvas) {
    74. if (mBitmap == null) {
    75. return;
    76. }
    77. if (mFillColor != Color.TRANSPARENT) {
    78. canvas.drawCircle(getWidth() / 2.0f, getHeight() / 2.0f, mDrawableRadius, mFillPaint);
    79. }
    80. canvas.drawCircle(getWidth() / 2.0f, getHeight() / 2.0f, mDrawableRadius, mBitmapPaint);
    81. if (mBorderWidth != 0) {
    82. canvas.drawCircle(getWidth() / 2.0f, getHeight() / 2.0f, mBorderRadius, mBorderPaint);
    83. }
    84. }
    85. @Override
    86. protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    87. super.onSizeChanged(w, h, oldw, oldh);
    88. setup();
    89. }
    90. public int getBorderColor() {
    91. return mBorderColor;
    92. }
    93. public void setBorderColor(@ColorInt int borderColor) {
    94. if (borderColor == mBorderColor) {
    95. return;
    96. }
    97. mBorderColor = borderColor;
    98. mBorderPaint.setColor(mBorderColor);
    99. invalidate();
    100. }
    101. public void setBorderColorResource(@ColorRes int borderColorRes) {
    102. setBorderColor(getContext().getResources().getColor(borderColorRes));
    103. }
    104. public int getFillColor() {
    105. return mFillColor;
    106. }
    107. public void setFillColor(@ColorInt int fillColor) {
    108. if (fillColor == mFillColor) {
    109. return;
    110. }
    111. mFillColor = fillColor;
    112. mFillPaint.setColor(fillColor);
    113. invalidate();
    114. }
    115. public void setFillColorResource(@ColorRes int fillColorRes) {
    116. setFillColor(getContext().getResources().getColor(fillColorRes));
    117. }
    118. public int getBorderWidth() {
    119. return mBorderWidth;
    120. }
    121. public void setBorderWidth(int borderWidth) {
    122. if (borderWidth == mBorderWidth) {
    123. return;
    124. }
    125. mBorderWidth = borderWidth;
    126. setup();
    127. }
    128. public boolean isBorderOverlay() {
    129. return mBorderOverlay;
    130. }
    131. public void setBorderOverlay(boolean borderOverlay) {
    132. if (borderOverlay == mBorderOverlay) {
    133. return;
    134. }
    135. mBorderOverlay = borderOverlay;
    136. setup();
    137. }
    138. @Override
    139. public void setImageBitmap(Bitmap bm) {
    140. super.setImageBitmap(bm);
    141. mBitmap = bm;
    142. setup();
    143. }
    144. @Override
    145. public void setImageDrawable(Drawable drawable) {
    146. super.setImageDrawable(drawable);
    147. mBitmap = getBitmapFromDrawable(drawable);
    148. setup();
    149. }
    150. @Override
    151. public void setImageResource(@DrawableRes int resId) {
    152. super.setImageResource(resId);
    153. mBitmap = getBitmapFromDrawable(getDrawable());
    154. setup();
    155. }
    156. @Override
    157. public void setImageURI(Uri uri) {
    158. super.setImageURI(uri);
    159. mBitmap = uri != null ? getBitmapFromDrawable(getDrawable()) : null;
    160. setup();
    161. }
    162. @Override
    163. public void setColorFilter(ColorFilter cf) {
    164. if (cf == mColorFilter) {
    165. return;
    166. }
    167. mColorFilter = cf;
    168. mBitmapPaint.setColorFilter(mColorFilter);
    169. invalidate();
    170. }
    171. private Bitmap getBitmapFromDrawable(Drawable drawable) {
    172. if (drawable == null) {
    173. return null;
    174. }
    175. if (drawable instanceof BitmapDrawable) {
    176. return ((BitmapDrawable) drawable).getBitmap();
    177. }
    178. try {
    179. Bitmap bitmap;
    180. if (drawable instanceof ColorDrawable) {
    181. bitmap = Bitmap.createBitmap(COLORDRAWABLE_DIMENSION, COLORDRAWABLE_DIMENSION, BITMAP_CONFIG);
    182. } else {
    183. bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), BITMAP_CONFIG);
    184. }
    185. Canvas canvas = new Canvas(bitmap);
    186. drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
    187. drawable.draw(canvas);
    188. return bitmap;
    189. } catch (Exception e) {
    190. e.printStackTrace();
    191. return null;
    192. }
    193. }
    194. private void setup() {
    195. if (!mReady) {
    196. mSetupPending = true;
    197. return;
    198. }
    199. if (getWidth() == 0 && getHeight() == 0) {
    200. return;
    201. }
    202. if (mBitmap == null) {
    203. invalidate();
    204. return;
    205. }
    206. mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
    207. mBitmapPaint.setAntiAlias(true);
    208. mBitmapPaint.setShader(mBitmapShader);
    209. mBorderPaint.setStyle(Paint.Style.STROKE);
    210. mBorderPaint.setAntiAlias(true);
    211. mBorderPaint.setColor(mBorderColor);
    212. mBorderPaint.setStrokeWidth(mBorderWidth);
    213. mFillPaint.setStyle(Paint.Style.FILL);
    214. mFillPaint.setAntiAlias(true);
    215. mFillPaint.setColor(mFillColor);
    216. mBitmapHeight = mBitmap.getHeight();
    217. mBitmapWidth = mBitmap.getWidth();
    218. mBorderRect.set(0, 0, getWidth(), getHeight());
    219. mBorderRadius = Math.min((mBorderRect.height() - mBorderWidth) / 2.0f, (mBorderRect.width() - mBorderWidth) / 2.0f);
    220. mDrawableRect.set(mBorderRect);
    221. if (!mBorderOverlay) {
    222. mDrawableRect.inset(mBorderWidth, mBorderWidth);
    223. }
    224. mDrawableRadius = Math.min(mDrawableRect.height() / 2.0f, mDrawableRect.width() / 2.0f);
    225. updateShaderMatrix();
    226. invalidate();
    227. }
    228. private void updateShaderMatrix() {
    229. float scale;
    230. float dx = 0;
    231. float dy = 0;
    232. mShaderMatrix.set(null);
    233. if (mBitmapWidth * mDrawableRect.height() > mDrawableRect.width() * mBitmapHeight) {
    234. scale = mDrawableRect.height() / mBitmapHeight;
    235. dx = (mDrawableRect.width() - mBitmapWidth * scale) * 0.5f;
    236. } else {
    237. scale = mDrawableRect.width() / mBitmapWidth;
    238. dy = (mDrawableRect.height() - mBitmapHeight * scale) * 0.5f;
    239. }
    240. mShaderMatrix.setScale(scale, scale);
    241. mShaderMatrix.postTranslate((int) (dx + 0.5f) + mDrawableRect.left, (int) (dy + 0.5f) + mDrawableRect.top);
    242. mBitmapShader.setLocalMatrix(mShaderMatrix);
    243. }
    244. }

    res目录下values创建添加

    <declare-styleable name="CircleImageView">
        <attr name="civ_border_width" format="dimension" />
        <attr name="civ_border_color" format="color" />
        <attr name="civ_border_overlay" format="boolean" />
        <attr name="civ_fill_color" format="color" />
    </declare-styleable>

    实现:

    主页布局

    1. <?xml version="1.0" encoding="utf-8"?>
    2. <androidx.drawerlayout.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
    3. xmlns:app="http://schemas.android.com/apk/res-auto"
    4. xmlns:tools="http://schemas.android.com/tools"
    5. android:id="@+id/drawer_layout"
    6. android:layout_width="match_parent"
    7. android:layout_height="match_parent"
    8. android:orientation="vertical"
    9. tools:context=".activity.MainActivity">
    10. <!--可以在程序中根据抽屉菜单 切换Fragment-->
    11. <FrameLayout
    12. android:id="@+id/frame_layout"
    13. android:layout_width="match_parent"
    14. android:layout_height="0dp"
    15. android:layout_weight="1" />
    16. <!--左边抽屉菜单宽高什么的可以自己调-->
    17. <com.google.android.material.navigation.NavigationView
    18. android:id="@+id/nv_menu_left"
    19. android:layout_width="140dp"
    20. android:layout_height="match_parent"
    21. android:layout_gravity="left"
    22. app:headerLayout="@layout/header"
    23. app:menu="@menu/menu_drawer_left" />
    24. </androidx.drawerlayout.widget.DrawerLayout>

    NavitationView常用属性

    android:layout_gravity="left"设置在哪边划出
    app:headerLayout="@layout/header"设置布局的文件头,此案例是那个圆形头像,昵称和uid
    app:menu="@menu/menu_drawer_left"设置点击项,就是档案馆那三个

    header布局

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="240dp"
        android:background="@mipmap/ic_app_top_bg"
        android:orientation="vertical">
    
        <CircleImageView
            android:layout_width="@dimen/dp_90"
            android:id="@+id/cv_user_head"
            android:layout_height="@dimen/dp_90"
            android:layout_marginLeft="@dimen/dp_20"
            android:layout_marginTop="36dp"
            android:layout_marginBottom="16dp"
            android:src="@mipmap/ic_app"/>
    
    
    
        <TextView
            android:id="@+id/tv_user_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="@dimen/dp_20"
            android:text="昵称"
            android:textColor="@color/white"
            android:textSize="@dimen/sp_14" />
    
        <LinearLayout
            android:id="@+id/ll_copy_uid"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="@dimen/dp_20"
            android:layout_marginTop="@dimen/dp_10"
            android:orientation="horizontal">
    
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="木偶MUO:"
                android:textColor="@color/color_212121"
                android:textSize="@dimen/sp_12" />
    
            <TextView
                android:id="@+id/tv_user_uuid"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text=""
                android:textColor="@color/color_212121"
                android:textSize="@dimen/sp_12" />
        </LinearLayout>
    
    </LinearLayout>

    menu_drawer_left布局【res目录下创建menu目录下创建menu_drawer_left布局】

    <?xml version="1.0" encoding="utf-8"?>
    <menu xmlns:android="http://schemas.android.com/apk/res/android">
        <item
            android:id="@+id/nav_home"
            android:icon="@mipmap/ic_app"
            android:title="关于我们" />
        <item
            android:id="@+id/nav_messages"
            android:icon="@mipmap/ic_fun"
            android:title="反馈" />
        <item
            android:id="@+id/nav_friends"
            android:icon="@mipmap/ic_muo"
            android:title="档案馆" />
    
    </menu>

    java代码

    public class MainActivity extends BaseActivity {

       
        @BindView(R.id.nv_menu_left)
        NavigationView nvMenuLeft;
        @BindView(R.id.drawer_layout)
        DrawerLayout drawerLayout;

        
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    //实例化的NavigationView控件
            nvMenuLeft.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
                @Override
                public boolean onNavigationItemSelected(@NonNull MenuItem item) {
                    switch (item.getItemId()) {

                        case R.id.nav_home:
                            startActivity(new Intent(MainActivity.this, AboutActivity.class));
                            break;
                        case R.id.nav_messages:
                            startActivity(new Intent(MainActivity.this, IdearActivity.class));
                            break;
                        case R.id.nav_friends:
                            startActivity(new Intent(MainActivity.this, TextImgActivity.class));
                            break;
                    }
                    drawerLayout.closeDrawers();
                    return false;
                }
            });

        }

        //抽屉布局的左侧头部初始化控件
        TextView mUserUid;
        LinearLayout llCopyUid;
        TextView mUserName;
       CircleImageView mUserAvatar;

        private void initUserInfo() {
            // 获取头部视图
            View headerView = nvMenuLeft.getHeaderView(0);
            //头部初始化控件
            mUserAvatar = headerView.findViewById(R.id.cv_user_head);
            mUserName = headerView.findViewById(R.id.tv_user_name);
            mUserUid = headerView.findViewById(R.id.tv_user_uuid);
            llCopyUid = headerView.findViewById(R.id.ll_copy_uid);
            mUserUid.setText(StringCache.get("uid"));
    //点击事件
            mUserAvatar.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    //关闭抽屉布局
                    drawerLayout.closeDrawers();
                //打开布局,参数Gravity.LEFTGravity.LEFT to move the left drawer or Gravity.RIGHT for the right.
         // GravityCompat.START or GravityCompat.END may also be used.
                   // drawerLayout.openDrawer(Gravity.LEFT);
                }
            });

    //头像展示
            Glide.with(MainActivity.this).load(R.mipmap.ic_app).apply(RequestOptions.circleCropTransform()).into(mUserAvatar);
        }

      
        }
    }

     

    大致的功能基本都在这边了,下面就是自己的逻辑实现了,应该是很明了的一篇文章了,后续需要的话可以继续补充功能

     

     

  • 相关阅读:
    DeltaLake技术学习与总结待续
    Java中时间类Date和Calendar的基本知识
    Java实现连接SQL Server解决方案及代码
    AntDB内存管理之内存上下文
    力扣每日一题43:字符串相乘
    Android Bitmap
    《嵌入式系统》知识总结12:SysTick定时器
    大模型时代,程序员的工作还是“写程序”?
    企业电子招投标采购系统——功能模块&功能描述+数字化采购管理 采购招投标
    GO语言网络编程(并发编程)Goroutine池
  • 原文地址:https://blog.csdn.net/qq_37328546/article/details/125619619