I have a menu, each item inside is a Framelayout, which is contain ImageView and TextView inside, the output looks like this screenshot:
RoundedCornerLayout.java is my class which extend FrameLayout to redraw it circular:
public class RoundedCornerLayout extends FrameLayout {
int borderColor;
Paint paint;
private Path path = new Path();
public RoundedCornerLayout(Context context, AttributeSet attrs) {
super(context, attrs);
borderColor = android.graphics.Color.rgb(128,0,129);
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(borderColor);
paint.setAntiAlias(true);
paint.setStrokeWidth(8);
paint.setStyle(Paint.Style.STROKE);
paint.setFlags(Paint.ANTI_ALIAS_FLAG);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
// compute the path
float halfWidth = w / 2f;
float halfHeight = h / 2f;
float centerX = halfWidth;
float centerY = halfHeight;
path.reset();
path.addCircle(centerX, centerY, Math.min(halfWidth, halfHeight), Path.Direction.CW);
path.close();
}
#Override
protected void dispatchDraw(Canvas canvas) {
int save = canvas.save();
canvas.clipPath(path);
super.dispatchDraw(canvas);
canvas.drawPath(path, paint);
canvas.restoreToCount(save);
}
}
Activity code:
<RoundedCornerLayout
android:id="#+id/viewClicked"
android:layout_width="110dp"
android:layout_height="110dp"
android:layout_marginRight="151dp"
app:layout_constraintRight_toRightOf="parent"
android:layout_marginEnd="8dp"
android:layout_marginLeft="8dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginBottom="50dp">
<ImageView
android:id="#+id/imgNature"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:srcCompat="#drawable/pic_nature" />
<TextView
android:id="#+id/lblClick"
android:layout_width="match_parent"
android:layout_height="39dp"
android:layout_marginTop="75dp"
android:background="#CC800081"
android:text="click"
android:textAlignment="center"
android:textColor="#ffffff" />
</RoundedCornerLayout>
what I got here is rounding the FrameLayout including all controls inside, but my problem is that rounding is not smooth as I expected!
Any idea how to edit the above code to get smooth circular FrameLayout ?
Please note that rounding through android:background="#drawable/circularlayout.xml didn't make the rounding over ImageView and TextView without RoundedCornerLayout.java
Related
I want to create a vertical seekbar with progress displaying near to thumb.
I created vertical seekbar by overriding Seekbar class and rotate the canvas. But I don't know how to make the text near to thumb.
#Override
protected final void onDraw(#NonNull final Canvas c) {
if (null == mPaint) {
mPaint = new Paint();
mPaint.setColor(Color.BLACK);
mPaint.setTextSize(50);
}
c.rotate(ROTATION_ANGLE);
c.translate(-getHeight(), 0);
super.onDraw(c);
Rect rect = getThumb().getBounds();
c.drawText(getProgress()+"%", rect.exactCenterX(), rect.exactCenterY(), mPaint);
}
So the problem is If I drawText like this the text also rotated.
So how to fix this?
Meanwhile, I tried some custom View implementation and I am a noob on that.
Update to Khemraj's answer
Actually changing the thumb is the simple trick as said by Khemraj. But the problem is when rotating the canvas all its contents will rotate(simple logic). When updating the thumb also will reflect this problem. So the simple thing is to make a rotated CustomTextView.
Method to make thumb
public Drawable getThumb(int progress) {
int width = getWidth();
((TextView) mThumbView.findViewById(R.id.tvProgress)).setText(String.format(Locale.getDefault(), "%d%%", progress));
mThumbView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
Bitmap bitmap = Bitmap.createBitmap(mThumbView.getMeasuredWidth(), mThumbView.getMeasuredHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
mThumbView.layout(0, 0, mThumbView.getMeasuredWidth(), mThumbView.getMeasuredHeight());
mThumbView.draw(canvas);
return new BitmapDrawable(getResources(), bitmap);
}
The layout
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center">
<ImageView
android:layout_marginTop="30dp"
android:id="#+id/imageView6"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/seek_thumb_gray"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.samsung.lighting.presentation.ui.custom_views.RotatedTextView
android:id="#+id/tvProgress"
android:layout_width="wrap_content"
android:layout_height="40dp"
android:gravity="center"
android:text="20%"
android:textColor="#000000"
android:textSize="12sp"
app:angle="90"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/imageView6" />
</android.support.constraint.ConstraintLayout>
The RotatedTextView
public class RotatedTextView extends AppCompatTextView {
private int mRotationAngle = 90;
#Override
protected void onDraw(Canvas canvas) {
if (mRotationAngle == 90) {
canvas.rotate(mRotationAngle);
canvas.translate(0, -getWidth());
} else if (mRotationAngle == -90) {
canvas.rotate(mRotationAngle);
canvas.translate(-getHeight(), 0);
}
super.onDraw(canvas);
}
}
So first we will rotate the Text to 90 or -90 degrees and set as thumb to VerticalSeekBar and in vertical seekbar rotate the canvas to 90 or -90 degrees. So finally we will get the actual result
Here I posted the working example.
Thanks, Khemraj
I am trying to build a custom view but there seems to be something that I am not getting.
I have overriden the onSizeChanged and onDraw methods and added my custom view in the activity layout and given it a height of say 100dp and pinned it to the bottom and start and end of the parent relative layout.
However the view doesn't render correctly and there is a blank empty space beneath it.
below is my onDraw and onSizedChanged methods
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
viewHeight = h;
viewWidth = w;
}
#Override
protected void onDraw(Canvas canvas) {
// draw background
painter.setStrokeWidth(viewHeight);
painter.setColor(Color.BLUE);
canvas.drawLine(0, 0, viewWidth, 0, painter);
}
below is how I am adding the view to the layout xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/activity_registration"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context="com.example.activities.RegistrationActivity">
<com.example.custom.widgets.MyCustomView
android:id="#+id/holder"
android:layout_alignParentEnd="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
android:layout_width="match_parent"
android:layout_height="100dp"/>
<FrameLayout
android:layout_above="#+id/holder"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:text="kjdgfjkasdgfjkahsdgfjkhsa"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
</RelativeLayout>
this is how it looks
FWIW, I also trying embedding my custom view inside a FrameLayout and explicitly setting the height of the FrameLayout and setting the height of my custom view to match_parent. still no success.
Overriding onMeasure() in your custom view class will set the size of your view dependent on the layout constraints provided by the parent.
This should give your custom view a height of 100dp:
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = Math.min(100, MeasureSpec.getSize(heightMeasureSpec));
setMeasuredDimension(width, height);
}
If you change the parent's constraints, you will need to change the MeasureSpec values that are passed into the method. See this question: https://stackoverflow.com/a/12267248/7395923
Change the onDraw() method to this:
protected void onDraw(Canvas canvas) {
// draw background
painter.setStrokeWidth(getWidth());
painter.setColor(Color.BLUE);
canvas.drawLine(0, 0, getWidth(), 0, painter);
}
And remove the onSizeChanged() method.
I'd like to make a shape similar to this one and use it as an image placeholder. I've learned how to make circles and rectangles using
canvas.drawRect(0,0,500,250,paint);
and from what I've browsed on here I gathered that I need to use paths as illustrated in this page. But I don't know where to begin when I look at the paths page previously linked. How do I create this custom shape, preferably to match the width of the screen? Thanks in advance!
This is my code so far to create a red rectangle that I'll eventually use to insert an image into:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Paint paint = new Paint();
paint.setColor(Color.parseColor("#CD5C5C"));
Bitmap bg = Bitmap.createBitmap(480,800, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bg);
canvas.drawRect(0,0,500,250,paint);
RelativeLayout rl = (RelativeLayout)
findViewById(R.id.activity_main);
rl.setBackgroundDrawable(new BitmapDrawable(bg));
}
I used the cheesesquare demo to test this out.
First I started with a custom view for the triangle.
Note that you have to add a color named background to your colors.xml -or- use the color you have configured for the list background instead.
public class TriangleView extends View {
private Path mPath = new Path();
private Paint mPaint = new Paint();
public TriangleView(Context context) {
super(context);
}
public TriangleView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public TriangleView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#TargetApi(21)
public TriangleView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
mPath.reset();
mPath.moveTo(0,0);
mPath.lineTo(0, getHeight());
mPath.lineTo(getWidth(), getHeight());
mPath.close();
int backgroundColor = getResources().getColor(R.color.background);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(backgroundColor);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawPath(mPath, mPaint);
}
}
Here's how I worked it into the layout. The triangle covers the bottom corner of the image. This is just the collapsing toolbar portion of the layout:
<android.support.design.widget.CollapsingToolbarLayout
android:id="#+id/collapsing_toolbar"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_scrollFlags="scroll|exitUntilCollapsed"
android:fitsSystemWindows="true"
app:contentScrim="?attr/colorPrimary"
app:expandedTitleMarginStart="48dp"
app:expandedTitleMarginEnd="64dp">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
app:layout_collapseMode="none">
<ImageView
android:id="#+id/backdrop"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop" />
<com.example.fixme.TriangleView
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_gravity="bottom"/>
</FrameLayout>
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:popupTheme="#style/ThemeOverlay.AppCompat.Light"
app:layout_collapseMode="pin" />
</android.support.design.widget.CollapsingToolbarLayout>
As long as you don't need parallax scrolling on the image, this seems to work okay.
I have a custom view, which I want to scale relative to the centre.
If I use this line of code for the ImageView it works perfectly well and scale correctly:
imageView.animate().scaleX(2.5f).scaleY(2.5f).setDuration(2000);
But if I use it for my custom view, it goes up, and animation doesn't look correctly, how to fix it?
Here is a video: https://youtu.be/f0-jMqE9ULU
(The animation of the red circle going wrong, animation of the pink circle (imageView) works correctly)
My custom view:
public class CircleDrawView extends View {
private Paint paint;
private int x;
private int y;
private String labelName;
private int radius = 40;
public CircleDrawView(Context context, AttributeSet attrs)
{
super(context, attrs);
}
public CircleDrawView(Context context)
{
super(context);
paint = new Paint();
}
public CircleDrawView(Context context, int x, int y, String labelName)
{
super(context);
paint = new Paint();
this.x=x;
this.y=y;
this.labelName=labelName;
}
#Override
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
paint.setColor(Color.RED);
canvas.drawCircle(x, y, radius, paint);
Paint textPaint = new Paint();
textPaint.setTextSize(25);
textPaint.setColor(Color.WHITE);
textPaint.setAntiAlias(true);
textPaint.setTextAlign(Paint.Align.CENTER);
Rect bounds = new Rect();
textPaint.getTextBounds(labelName, 0, labelName.length(), bounds);
canvas.drawText(labelName, x, y, textPaint);
}
}
Activity:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
LinearLayout rootView =(LinearLayout) findViewById(R.id.test_layout);
ImageView imageView = (ImageView) findViewById(R.id.test_image);
CircleDrawView circle = new CircleDrawView(getApplicationContext(), 200, 200, "1");
rootView.addView(circle);
rootView.invalidate();
circle.animate().scaleX(1.2f).scaleY(1.2f).setDuration(2000);
imageView.animate().scaleX(2.5f).scaleY(2.5f).setDuration(2000);
}
}
xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context="com.example.attracti.animation.MainActivity">
<LinearLayout
android:id="#+id/test_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<ImageView
android:id="#+id/test_image"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_gravity="center_vertical"
android:layout_marginLeft="100dp"
android:background="#drawable/circle_shape" />
</LinearLayout>
</RelativeLayout>
Your call to circle.animate() returns a ViewPropertyAnimator.
The scaleX() and scaleY() methods you call on this ViewPropertyAnimator scale the View about it's pivot.
The pivot of a View is set by the View.setPivotX() and View.setPivotY() methods.
In your constructor, set these values to the supplied x and y. Your view will then scale about the supplied center point.
I have a TableLayout with 6 childs/entrys. These childs are a custom RelativeLayout. In each RelativeLayout is a big TextView in the middle and an ImageView and small TextView at the bottom.
The ImageView should be as tall as the TextView next to it. That's why I set the attribute ALIGN_TOP and ALIGN_BOTTOM to the TextView (you can see it in code below). This works very well and both - ImageView and TextView - have the same height now. But the problem is, that the left and right side of the ImageView don't "wrap content" anymore (as you can see on the screenshot).
Is there a way to fit the left and right side to the image and remove the "padding"?
Here is my code:
view_display_component.xml
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android" >
<TextView
android:id="#+id/tvDisplayBig"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:layout_weight="1"
android:gravity="center"
android:textColor="#color/white"
android:textSize="#dimen/font_size_extra_large" />
<ImageView
android:id="#+id/imageViewDisplayIcon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_below="#id/tvDisplayBig"
android:layout_gravity="bottom"
android:adjustViewBounds="true"
android:baselineAlignBottom="true"
android:scaleType="fitCenter"
android:src="#drawable/stopwatch_64"
android:visibility="visible" />
<TextView
android:id="#+id/tvDisplaySmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:gravity="bottom"
android:includeFontPadding="false"
android:textColor="#color/white"
android:textSize="#dimen/font_size_small" />
</merge>
class DisplayComponent which extends RelativLayout
public DisplayComponent(Context context) {
super(context);
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.view_display_component, this, true);
tvDisplay = (TextView) getChildAt(0);
icon = (ImageView) getChildAt(1);
tvName = (TextView) getChildAt(2);
setupAlign();
}
private void setupAlign() {
if(index % 2 == 0) { // LEFT SIDE
// same as "RIGHT SIDE"
} else { // RIGHT SIDE
RelativeLayout.LayoutParams paramsIcon = (RelativeLayout.LayoutParams) icon.getLayoutParams();
paramsIcon.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
paramsIcon.addRule(RelativeLayout.ALIGN_TOP, tvName.getId());
paramsIcon.addRule(RelativeLayout.ALIGN_BOTTOM, tvName.getId());
icon.setLayoutParams(paramsIcon);
RelativeLayout.LayoutParams paramsTvName = (RelativeLayout.LayoutParams) tvName.getLayoutParams();
paramsTvName.addRule(RelativeLayout.RIGHT_OF, icon.getId());
tvName.setLayoutParams(paramsTvName);
tvName.setBackgroundColor(Color.BLUE); // only for testing
icon.setBackgroundColor(Color.YELLOW);
}
I found an (ugly) solution. Because my icon is square, I created a custom ImageView and overrode the onSizeChanged() method like this:
public class IconImageView extends ImageView {
public IconImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if(h != oldh && h > 0)
getLayoutParams().width = h; // same width as height
}
}
But this works only if the image is square. That's why I am still searching for a better solution. Maybe some layout solution with a better setting of alignment.
Best regards!