I am using a RoundedNetworkImageView.I want to add an border of 1 dp to it.If i create a separated drawable and put it as background to RoundedNetworkImageView then it replaces the roundedness of view.How can i update below code to add border to imageview?
Below is the code I am using-
public class RoundedNetworkImageView extends NetworkImageView {
public CWRoundedNetworkImageView(Context context) {
super(context);
}
public CWRoundedNetworkImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CWRoundedNetworkImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, widthMeasureSpec);
}
#Override
public ScaleType getScaleType() {
return ScaleType.CENTER_CROP;
}
#Override
protected void onDraw(Canvas canvas) {
float radius = 12.0f;
Path clipPath = new Path();
RectF rect = new RectF(0, 0, this.getWidth(), this.getHeight());
clipPath.addRoundRect(rect, radius, radius, Path.Direction.CW);
canvas.clipPath(clipPath);
super.onDraw(canvas);
}
}
Drawable not drawing in custom TextView. Can anyone please help me in sorting the problem out. What i was trying to build is a texview that shows progress. The below mention code is just a sample.
public class ProgressableTextview extends TextView {
Paint mPaint;
int mProgress = 70;
int maxProgress = 100;
int mRadiud = 10;
public ProgressableTextview(Context context) {
super(context);
init();
}
public ProgressableTextview(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public ProgressableTextview(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
public void init(){
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setColor(Color.BLACK);
mPaint.setStyle(Paint.Style.FILL);
}
#Override
public void onDraw(Canvas canvas) {
canvas.drawColor(Color.GRAY);
canvas.drawRect(0, 0, (mProgress/maxProgress)*getWidth(), getMeasuredHeight(), mPaint);
canvas.save();
canvas.translate(getLeft(), 0);
super.onDraw(canvas);
canvas.restore();
}
}
Ok i figured out what was the problem, it was the mistake in drawRect function. drawRect function need to changed as :
canvas.drawRect(0, 0, ((float) mProgress/maxProgress) * getWidth(), getMeasureHeight(), mPaint);
since mProgress was a integer dividing it by integer returns zero and that was the problem.
I have CircleView custom object, that works fine.
I want to compound it with a TextView below, like this:
So I made CircleCheckBoxWithText that compound the two views,
The problem is that if the first view that I'm adding is my custom view, and the second view is the TextView, I can't see the TextView.
(When the first view that I'm adding is the TextView and the second is my custom view, I can see both views)
CircleView:
public class CircleView extends View
{
private float mDimension;
private int mFillColor;
private int mBorderColor;
private boolean mIsChecked;
private Paint mFillPaint = new Paint();
private Paint mStrokePaint = new Paint();
public CircleView(Context context)
{
super(context);
}
public CircleView(Context context, AttributeSet attrs, int defStyleAttr)
{
super(context, attrs, defStyleAttr);
}
public CircleView(Context context, AttributeSet attrs)
{
super(context, attrs);
// Get the styledAttributes from the xml
TypedArray styledAttributes = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CircleView, 0, 0);
try
{
mDimension = styledAttributes.getDimensionPixelOffset(R.styleable.CircleView_dim, 20);
mFillColor = styledAttributes.getColor(R.styleable.CircleView_fill_color, Color.WHITE);
mBorderColor = styledAttributes.getColor(R.styleable.CircleView_border_color, Color.WHITE);
mIsChecked = styledAttributes.getBoolean(R.styleable.CircleView_checked, false);
}
finally
{
styledAttributes.recycle();
}
}
#Override
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
// init canvas
canvas.drawARGB(0, 0, 0, 0);
// Set the Fill
mFillPaint.setAntiAlias(true);
mFillPaint.setDither(true);
mFillPaint.setColor(mFillColor);
// Set the Stroke
mStrokePaint.setAntiAlias(true);
mStrokePaint.setDither(true);
mStrokePaint.setColor(mBorderColor);
mStrokePaint.setStyle(Paint.Style.STROKE);
mStrokePaint.setStrokeWidth(8f);
canvas.drawCircle(mDimension / 2, mDimension / 2, mDimension / 2 - 0.2f, mFillPaint);
if(mIsChecked)
{
canvas.drawCircle(mDimension / 2, mDimension / 2, mDimension / 2 - mStrokePaint.getStrokeWidth() / 2 + 0.1f, mStrokePaint);
}
}
}
CircleCheckBoxWithText:
public class CircleCheckBoxWithText extends LinearLayout
{
public CircleCheckBoxWithText(Context context)
{
super(context);
setOrientation(VERTICAL);
}
public CircleCheckBoxWithText(Context context, AttributeSet attrs)
{
super(context, attrs);
setOrientation(VERTICAL);
addView(new CircleView(context, attrs));
TextView textView = new TextView(context);
textView.setGravity(Gravity.CENTER);
textView.setText("TEST");
addView(textView);
}
public CircleCheckBoxWithText(Context context, AttributeSet attrs, int defStyleAttr)
{
super(context, attrs, defStyleAttr);
setOrientation(VERTICAL);
}
}
I have a custom view which displays a star shape by using a path. This view works as expected, but now I want to shift it's implementation to the new Google Material recommendation.
Unfortunately elevation depends on a convex outline, and I haven't found a solution yet.
Are there any known workarounds or any other creative solution that any of you know?
This is my concave path:
double outerSize = w / 2;
double innerSize = w / 5;
double delta = 2.0*Math.PI/5.0;
double rotation = Math.toRadians(-90);
double xpos = w/2.0;
double ypos = h/2.0;
mPath = new Path();
mPath.moveTo((float)(outerSize * Math.cos(delta + rotation) + xpos),
(float)(outerSize * Math.sin(delta + rotation) + ypos));
for(int point= 0;point<6;point++)
{
mPath.lineTo((float) (innerSize * Math.cos(delta * (point + 0.5) + rotation) + xpos),
(float) (innerSize * Math.sin(delta * (point + 0.5) + rotation) + ypos));
mPath.lineTo((float) (outerSize * Math.cos(delta * (point + 1.0) + rotation) + xpos),
(float) (outerSize * Math.sin(delta * (point + 1.0) + rotation) + ypos));
}
mPath.close();
I've tried this code, without success, which works fine on convex views.
#TargetApi(21)
private class StarOutline extends ViewOutlineProvider {
#Override
public void getOutline(View view, Outline outline) {
StartView r = (StartView) view;
// i know here say setConvexPath not setConcavePath
outline.setConvexPath(r.mPath);
}
}
But as expected, I'm getting an exception:
java.lang.IllegalArgumentException: path must be convex
at android.graphics.Outline.setConvexPath(Outline.java:216)
Any idea how to achieve this aim?
As some of the comments and answer point out, native android shadow only works with convex outlines.
So you are left with either drawing a fake shadow manually on your own (via canvas, bitmap etc) or rely on someone else's library to draw fake shadow for you (Google's Material Components library etc).
Are there any known workarounds or any other creative solution that any of you know?
If you must rely on native android shadow, you can try to break down the shape into multiple convex shape and draw these individually.
Here is an example:
I broke down the star shape into 1 pentagon and 5 triangle polygons (all of them have convex outline) and draw them individually.
TriangleView:
public class TriangleView extends View {
private final Path path = new Path();
private final Paint paint = new Paint();
public TriangleView(Context context) {
super(context);
init(context, null, 0,0);
}
public TriangleView(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
init(context, attrs, 0,0);
}
public TriangleView(Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs, defStyleAttr, 0);
}
public TriangleView(Context context, #Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(context, attrs, defStyleAttr, defStyleRes);
}
private void init(Context context, #Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes){
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.argb(255, 100, 100, 255));
setOutlineProvider(new OutlineProvider());
}
public void setPoints(float x1, float y1, float x2, float y2, float x3, float y3){
path.reset();
path.moveTo(x1, y1);
path.lineTo(x2, y2);
path.lineTo(x3, y3);
path.close();
postInvalidate();
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawPath(path, paint);
}
private static class OutlineProvider extends ViewOutlineProvider{
#Override
public void getOutline(View view, Outline outline) {
Path path = ((TriangleView)view).path;
outline.setConvexPath(path);
}
}
}
PentagonView:
public class PentagonView extends View {
private final Path path = new Path();
private final Paint paint = new Paint();
public PentagonView(Context context) {
super(context);
init(context, null, 0,0);
}
public PentagonView(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
init(context, attrs, 0,0);
}
public PentagonView(Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs, defStyleAttr, 0);
}
public PentagonView(Context context, #Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(context, attrs, defStyleAttr, defStyleRes);
}
private void init(Context context, #Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes){
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.argb(255, 150, 150, 255));
setOutlineProvider(new OutlineProvider());
}
public void setPoints(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, float x5, float y5){
path.reset();
path.moveTo(x1, y1);
path.lineTo(x2, y2);
path.lineTo(x3, y3);
path.lineTo(x4, y4);
path.lineTo(x5, y5);
path.close();
postInvalidate();
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawPath(path, paint);
}
private static class OutlineProvider extends ViewOutlineProvider {
#Override
public void getOutline(View view, Outline outline) {
Path path = ((PentagonView)view).path;
outline.setConvexPath(path);
}
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<app.eccweizhi.concaveshadow.PentagonView
android:id="#+id/pentagonView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:elevation="4dp" />
<app.eccweizhi.concaveshadow.TriangleView
android:id="#+id/triangle1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:elevation="4dp" />
<app.eccweizhi.concaveshadow.TriangleView
android:id="#+id/triangle2"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:elevation="4dp" />
<app.eccweizhi.concaveshadow.TriangleView
android:id="#+id/triangle3"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:elevation="4dp" />
<app.eccweizhi.concaveshadow.TriangleView
android:id="#+id/triangle4"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:elevation="4dp" />
<app.eccweizhi.concaveshadow.TriangleView
android:id="#+id/triangle5"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:elevation="4dp" />
</FrameLayout>
I then use them like this
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
pentagonView.setPoints(
520f,
520f,
640f,
520f,
677.0818f,
634.1266f,
580f,
704.6608f,
482.9182f,
634.1266f
)
triangle1.setPoints(520f, 520f, 640f, 520f, 580f, 400f)
triangle2.setPoints(640f, 520f, 677.0818f, 634.1266f, 777f, 520f)
triangle3.setPoints(677.0818f, 634.1266f, 580f, 704.6608f, 697f, 750f)
triangle4.setPoints(580f, 704.6608f, 482.9182f, 634.1266f, 440f, 750f)
triangle5.setPoints(482.9182f, 634.1266f, 520f, 520f, 400f, 520f)
}
}
There is a new drawable called MaterialShapeDrawable in AndroidX package. Given a path, it can render shadow to both concave and convex shapes.
https://developer.android.com/reference/com/google/android/material/shape/MaterialShapeDrawable
This is how you would provide shadow to your concave shape WITHOUT MaterialShapeDrawable:
Create a new bitmap
Modify the bitmap ( draw star shape path on it using a new Canvas object )
Blur the bitmap so it actually looks like a shadow. ( Blurring should be done with RenderScript for performance reasons )
Draw the bitmap on views Canvas.
I am trying to make custom Views in android I mention all my arrtrs list in MainActivity and the XML file but I there is an error in main.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:custom="http://schemas.android.com/apk/res/com.example.customviews.piechart"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.example.customviews.charting.piechart.MainActivity
android:layout_width="match_parent"
android:layout_height="?android:attr/listPreferredItemHeight"
custom:titleText="Background color"
custom:valueColor="#android:color/holo_green_light"
/>
</LinearLayout>
Do you have a constructor with no arguments?
You should add a constructor like:
public MainActivity(Context context, AttributeSet attrs) {
super(context, attrs);
}
or
public MainActivity(Context context) {
super(context);
}
You asked for a simple example of a custom view:
public class BirdView extends ImageView {
private float direction = 0;
Paint paint = new Paint();
public BirdView(Context context) {
super(context);
}
public BirdView(Context context, AttributeSet attrs) {
super(context, attrs);
paint.setColor(Color.WHITE);
paint.setStrokeWidth(6);
}
public BirdView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec),
MeasureSpec.getSize(heightMeasureSpec));
}
#Override
public void draw(Canvas canvas) {
canvas.save();
int stratX = getWidth() / 2;
int startY = getHeight() / 2;
canvas.rotate(direction, stratX, startY);
super.draw(canvas);
canvas.restore();
canvas.drawLine(stratX, startY, stratX, startY + 80, paint);
}
public void update(float dir) {
direction = dir;
invalidate();
}
}