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.


/** 
圆形头像
 * */
public class CircleImageView extends ImageView {

	private static final ScaleType SCALE_TYPE = ScaleType.CENTER_CROP;

	private static final Bitmap.Config BITMAP_CONFIG = Bitmap.Config.ARGB_8888;
	private static final int COLORDRAWABLE_DIMENSION = 2;

	private static final int DEFAULT_BORDER_WIDTH = 0;
	private static final int DEFAULT_BORDER_COLOR = Color.BLACK;
	private static final int DEFAULT_FILL_COLOR = Color.TRANSPARENT;
	private static final boolean DEFAULT_BORDER_OVERLAY = false;

	private final RectF mDrawableRect = new RectF();
	private final RectF mBorderRect = new RectF();

	private final Matrix mShaderMatrix = new Matrix();
	private final Paint mBitmapPaint = new Paint();
	private final Paint mBorderPaint = new Paint();
	private final Paint mFillPaint = new Paint();

	private int mBorderColor = DEFAULT_BORDER_COLOR;
	private int mBorderWidth = DEFAULT_BORDER_WIDTH;
	private int mFillColor = DEFAULT_FILL_COLOR;

	private Bitmap mBitmap;
	private BitmapShader mBitmapShader;
	private int mBitmapWidth;
	private int mBitmapHeight;

	private float mDrawableRadius;
	private float mBorderRadius;

	private ColorFilter mColorFilter;

	private boolean mReady;
	private boolean mSetupPending;
	private boolean mBorderOverlay;

	public CircleImageView(Context context) {
		super(context);

		init();
	}

	public CircleImageView(Context context, AttributeSet attrs) {
		this(context, attrs, 0);
	}

	public CircleImageView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);

		TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CircleImageView, defStyle, 0);

		mBorderWidth = a.getDimensionPixelSize(R.styleable.CircleImageView_civ_border_width, DEFAULT_BORDER_WIDTH);
		mBorderColor = a.getColor(R.styleable.CircleImageView_civ_border_color, DEFAULT_BORDER_COLOR);
		mBorderOverlay = a.getBoolean(R.styleable.CircleImageView_civ_border_overlay, DEFAULT_BORDER_OVERLAY);
		mFillColor = a.getColor(R.styleable.CircleImageView_civ_fill_color, DEFAULT_FILL_COLOR);

		a.recycle();

		init();
	}

	private void init() {
		super.setScaleType(SCALE_TYPE);
		mReady = true;

		if (mSetupPending) {
			setup();
			mSetupPending = false;
		}
	}

	@Override
	public ScaleType getScaleType() {
		return SCALE_TYPE;
	}

	@Override
	public void setScaleType(ScaleType scaleType) {
		if (scaleType != SCALE_TYPE) {
			throw new IllegalArgumentException(String.format("ScaleType %s not supported.", scaleType));
		}
	}

	@Override
	public void setAdjustViewBounds(boolean adjustViewBounds) {
		if (adjustViewBounds) {
			throw new IllegalArgumentException("adjustViewBounds not supported.");
		}
	}

	@Override
	protected void onDraw(Canvas canvas) {
		if (mBitmap == null) {
			return;
		}

		if (mFillColor != Color.TRANSPARENT) {
			canvas.drawCircle(getWidth() / 2.0f, getHeight() / 2.0f, mDrawableRadius, mFillPaint);
		}
		canvas.drawCircle(getWidth() / 2.0f, getHeight() / 2.0f, mDrawableRadius, mBitmapPaint);
		if (mBorderWidth != 0) {
			canvas.drawCircle(getWidth() / 2.0f, getHeight() / 2.0f, mBorderRadius, mBorderPaint);
		}
	}

	@Override
	protected void onSizeChanged(int w, int h, int oldw, int oldh) {
		super.onSizeChanged(w, h, oldw, oldh);
		setup();
	}

	public int getBorderColor() {
		return mBorderColor;
	}

	public void setBorderColor(@ColorInt int borderColor) {
		if (borderColor == mBorderColor) {
			return;
		}

		mBorderColor = borderColor;
		mBorderPaint.setColor(mBorderColor);
		invalidate();
	}

	public void setBorderColorResource(@ColorRes int borderColorRes) {
		setBorderColor(getContext().getResources().getColor(borderColorRes));
	}

	public int getFillColor() {
		return mFillColor;
	}

	public void setFillColor(@ColorInt int fillColor) {
		if (fillColor == mFillColor) {
			return;
		}

		mFillColor = fillColor;
		mFillPaint.setColor(fillColor);
		invalidate();
	}

	public void setFillColorResource(@ColorRes int fillColorRes) {
		setFillColor(getContext().getResources().getColor(fillColorRes));
	}

	public int getBorderWidth() {
		return mBorderWidth;
	}

	public void setBorderWidth(int borderWidth) {
		if (borderWidth == mBorderWidth) {
			return;
		}

		mBorderWidth = borderWidth;
		setup();
	}

	public boolean isBorderOverlay() {
		return mBorderOverlay;
	}

	public void setBorderOverlay(boolean borderOverlay) {
		if (borderOverlay == mBorderOverlay) {
			return;
		}

		mBorderOverlay = borderOverlay;
		setup();
	}

	@Override
	public void setImageBitmap(Bitmap bm) {
		super.setImageBitmap(bm);
		mBitmap = bm;
		setup();
	}

	@Override
	public void setImageDrawable(Drawable drawable) {
		super.setImageDrawable(drawable);
		mBitmap = getBitmapFromDrawable(drawable);
		setup();
	}

	@Override
	public void setImageResource(@DrawableRes int resId) {
		super.setImageResource(resId);
		mBitmap = getBitmapFromDrawable(getDrawable());
		setup();
	}

	@Override
	public void setImageURI(Uri uri) {
		super.setImageURI(uri);
		mBitmap = uri != null ? getBitmapFromDrawable(getDrawable()) : null;
		setup();
	}

	@Override
	public void setColorFilter(ColorFilter cf) {
		if (cf == mColorFilter) {
			return;
		}

		mColorFilter = cf;
		mBitmapPaint.setColorFilter(mColorFilter);
		invalidate();
	}

	private Bitmap getBitmapFromDrawable(Drawable drawable) {
		if (drawable == null) {
			return null;
		}

		if (drawable instanceof BitmapDrawable) {
			return ((BitmapDrawable) drawable).getBitmap();
		}

		try {
			Bitmap bitmap;

			if (drawable instanceof ColorDrawable) {
				bitmap = Bitmap.createBitmap(COLORDRAWABLE_DIMENSION, COLORDRAWABLE_DIMENSION, BITMAP_CONFIG);
			} else {
				bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), BITMAP_CONFIG);
			}

			Canvas canvas = new Canvas(bitmap);
			drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
			drawable.draw(canvas);
			return bitmap;
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	private void setup() {
		if (!mReady) {
			mSetupPending = true;
			return;
		}

		if (getWidth() == 0 && getHeight() == 0) {
			return;
		}

		if (mBitmap == null) {
			invalidate();
			return;
		}

		mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);

		mBitmapPaint.setAntiAlias(true);
		mBitmapPaint.setShader(mBitmapShader);

		mBorderPaint.setStyle(Paint.Style.STROKE);
		mBorderPaint.setAntiAlias(true);
		mBorderPaint.setColor(mBorderColor);
		mBorderPaint.setStrokeWidth(mBorderWidth);

		mFillPaint.setStyle(Paint.Style.FILL);
		mFillPaint.setAntiAlias(true);
		mFillPaint.setColor(mFillColor);

		mBitmapHeight = mBitmap.getHeight();
		mBitmapWidth = mBitmap.getWidth();

		mBorderRect.set(0, 0, getWidth(), getHeight());
		mBorderRadius = Math.min((mBorderRect.height() - mBorderWidth) / 2.0f, (mBorderRect.width() - mBorderWidth) / 2.0f);

		mDrawableRect.set(mBorderRect);
		if (!mBorderOverlay) {
			mDrawableRect.inset(mBorderWidth, mBorderWidth);
		}
		mDrawableRadius = Math.min(mDrawableRect.height() / 2.0f, mDrawableRect.width() / 2.0f);

		updateShaderMatrix();
		invalidate();
	}

	private void updateShaderMatrix() {
		float scale;
		float dx = 0;
		float dy = 0;

		mShaderMatrix.set(null);

		if (mBitmapWidth * mDrawableRect.height() > mDrawableRect.width() * mBitmapHeight) {
			scale = mDrawableRect.height() / mBitmapHeight;
			dx = (mDrawableRect.width() - mBitmapWidth * scale) * 0.5f;
		} else {
			scale = mDrawableRect.width() / mBitmapWidth;
			dy = (mDrawableRect.height() - mBitmapHeight * scale) * 0.5f;
		}

		mShaderMatrix.setScale(scale, scale);
		mShaderMatrix.postTranslate((int) (dx + 0.5f) + mDrawableRect.left, (int) (dy + 0.5f) + mDrawableRect.top);

		mBitmapShader.setLocalMatrix(mShaderMatrix);
	}

}

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>

实现:

主页布局

<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".activity.MainActivity">


    <!--可以在程序中根据抽屉菜单 切换Fragment-->


        <FrameLayout
            android:id="@+id/frame_layout"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1" />

      
    <!--左边抽屉菜单宽高什么的可以自己调-->
    <com.google.android.material.navigation.NavigationView
        android:id="@+id/nv_menu_left"
        android:layout_width="140dp"
        android:layout_height="match_parent"
        android:layout_gravity="left"
        app:headerLayout="@layout/header"
        app:menu="@menu/menu_drawer_left" />


</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);
    }

  
    }
}

 

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

 

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值