my goal is to support a design that has a top bar made of a custom view with the bottom edge defined by two bezier curves. i have extended ImageView and defined a path that encompasses an area that I would like to delete (or make transparent). I'm new to working with graphics in android; is what i'm trying to do even possible? here is what i have done so far:
here is the class:
import android.app.Activity; import android.content.Context; import android.graphics.Canvas; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Path; import android.graphics.PointF; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.Log; import androidx.annotation.Nullable; public class BezierCroppedImageView extends androidx.appcompat.widget.AppCompatImageView { private static final String LOG_TAG = BezierCroppedImageView.class.getSimpleName(); Paint mPaint; Path mPath; PointF[] points = new PointF[8]; DisplayMetrics dm = new DisplayMetrics(); public BezierCroppedImageView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); } protected void onDraw(Canvas canvas) { Log.w(LOG_TAG, "drawing bezier"); super.onDraw(canvas); mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeWidth((float) 2.5); mPaint.setPathEffect(null); canvas.drawPaint(mPaint); mPath.moveTo(points[0].x, points[0].y); mPath.quadTo(points[1].x, points[1].y, points[2].x, points[2].y); mPath.lineTo(points[3].x, points[3].y); mPath.quadTo(points[4].x, points[4].y, points[5].x, points[5].y); mPath.lineTo(points[6].x, points[6].y); mPath.lineTo(points[7].x, points[7].y); mPath.close(); mPaint.setColor(getResources().getColor(android.R.color.holo_green_light)); canvas.drawPath(mPath, mPaint); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); mPath = new Path(); mPaint = new Paint(); float[] f = new float[9]; getImageMatrix().getValues(f); final float scaleX = f[Matrix.MSCALE_X]; final float scaleY = f[Matrix.MSCALE_Y]; final Drawable d = getDrawable(); final int origW = d.getIntrinsicWidth(); final int origH = d.getIntrinsicHeight(); final int actW = Math.round(origW * scaleX); final int actH = Math.round(origH * scaleY); ((Activity) getContext()).getWindowManager().getDefaultDisplay().getMetrics(dm); Log.w(LOG_TAG, "measuring bezier!!; w/h: "+dm.widthPixels+"/"+dm.heightPixels); Log.w(LOG_TAG, "orig w/h: "+origW+"/"+origH+"; actual w/h: "+actW+"/"+actH+"; scale w/h: "+scaleX+"/"+scaleY); float width = dm.widthPixels; float height = dm.heightPixels; points[0] = new PointF(0, (float) (0.3 * height)); points[1] = new PointF(0, (float) (0.2 * height)); points[2] = new PointF((float) (0.1 * height), (float) (0.2 * height)); points[3] = new PointF((float) (width - 0.1 * height), (float) (0.2 * height)); points[4] = new PointF(width, (float) (0.2 * height)); points[5] = new PointF(width, (float) (0.1 * height)); points[6] = new PointF(width, height); points[7] = new PointF(0, height); setMeasuredDimension(widthMeasureSpec, heightMeasureSpec); } }
and here is the layout file
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent"> <androidx.constraintlayout.widget.Guideline android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" app:layout_constraintGuide_begin="90dp" android:id="@+id/header_guide_top"/> <androidx.constraintlayout.widget.Guideline android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" app:layout_constraintGuide_begin="120dp" android:id="@+id/header_guide_bot"/> <androidx.swiperefreshlayout.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/swiperefresh" app:layout_constraintTop_toBottomOf="@id/header_guide_top" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" android:layout_width="match_parent" android:layout_height="wrap_content"> <androidx.coordinatorlayout.widget.CoordinatorLayout android:id="@+id/coordinator_layout" android:layout_width="match_parent" android:layout_height="match_parent"> <FrameLayout android:id="@+id/frame_layout" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior"/> </androidx.coordinatorlayout.widget.CoordinatorLayout> </androidx.swiperefreshlayout.widget.SwipeRefreshLayout> <com.collinsresearch.flameboss.BezierCroppedImageView android:onClick="bezierClicked" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" android:id="@+id/bezier_test_image" android:layout_width="match_parent" android:layout_height="match_parent" android:src="@drawable/pexels" android:scaleType="centerCrop"/> <com.google.android.material.bottomnavigation.BottomNavigationView android:id="@+id/navigation" android:layout_width="match_parent" android:layout_height="wrap_content" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" android:background="@android:color/holo_green_light" app:itemIconTint="@color/ic_on" app:itemTextColor="@color/bottom_navigation_view_selector" app:itemIconSize="@dimen/nav_ic_size" app:menu="@menu/navigation" android:visibility="visible" android:translationY="25dp"/> </androidx.constraintlayout.widget.ConstraintLayout>
as you can see, my strategy is to use a ConstraintLayout that contains the BottomNavigationView, a SwipeRefreshLayout that will contain each fragment, and put my BezierCroppedImageView constrained to the top with a transparent area exposing the SwipeRefreshLayout. I also plan to add some buttons to the top view but I'm having trouble with the transparency.
I've been looking at examples and documentation but I haven't found anything helpful. I looked into canvas.clipToPath but that's only supported after API level 28 and only supports oval and rectangular paths. I've also taken a look at the different PorterDuff modes but I'm not even sure if that's relevant here.
I was only able to get this far using this documentation https://developer.android.com/guide/topics/ui/custom-components and various stackoverflow articles. I appreciate any thoughts, comments, or thought-provoking questions. Thanks in advance for you time!!
https://stackoverflow.com/questions/67327687/delete-or-make-transparent-canvas-area-enclosed-by-a-path April 30, 2021 at 11:06AM
没有评论:
发表评论