I'm looking to replicate the following within my application:
As you can see, its basically a button which increases/decreases the value of the text view contained within it. This button will have three visual states -> unpressed, decrease and increase (as seen in the image above, the user taps the increase arrows and the button appears pressed in on that side)
Here are my 3 button states currently:
As you can see, the problem I have is being able to correctly skew/rotate the text view so it looks visually correct and appears slanted along with the button when its being increased or decreased.
I have tried two different approaches so far:
Create a custom text view class which overrides the onDraw() method to skew the canvas:
#Override
public void onDraw(Canvas canvas) {
canvas.save();
canvas.skew(0.2f, 0f);
super.onDraw(canvas);
canvas.restore();
}
Integrate the Rotate3dAnimation class (source here) and used many different variations to get the desired result such as:
Rotate3dAnimation skew = new Rotate3dAnimation(
30, 0, centerX, centerY, 0, false);
txtAmount.startAnimation(skew);
Unfortunately, I'm not quite getting the exact result that mirrors the first image above. I'm getting confused with setting values with the Z-axis, skew, rotate etc.
I'd greatly appreciate any help from anyone who has experience with this stuff. Thanks in advance
Well I even tried and I came up with something like this:
public class DemoActivity extends TextView {
Context context;
String firstText = "$120.00";
public DemoActivity(Context context)
{
super(context);
this.context = context;
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
setText(firstText);
setTextSize(30);
canvas.skew(1.0f, 0.3f); //you need to change values over here
Rotate3dAnimation skew = new Rotate3dAnimation(
-20, 30,200, 200, 0, false); //here too
startAnimation(skew);
}
}
I got an output as:
I guess changing the values by trial and error can solve your problem.
Hope it helps.
Thanks to Parth Doshi answer. His answer need a little tweaking to run which I'm sharing here to save someone else time.
First create a class in src folder and write all of three constructors.
public class TextViewDemo extends TextView {
Context context;
String text = "TESTING 3DX TOOLS";
public TextViewDemo(Context context) {
super(context);
this.context = context;
}
public TextViewDemo(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
}
public TextViewDemo(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this.context = context;
}
#Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
super.onDraw(canvas);
setText(text);
setTextSize(30);
canvas.skew(0.5f, 1.0f); // you need to change values over here
Rotate3dAnimation skew = new Rotate3dAnimation(-50, 30, 0, 0, 0,
false); // here too
startAnimation(skew);
}
}
In you res/layout/my_layout.xml file you can add a tag of your custom made TextView.
<com.yourpackage.name.TextViewDemo
android:id="#+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:text="Hello World"
<!-- All parameters and value shall remain same -->
/>
Like any other view, you can create an instance of TextViewDemo in your onCreate() method
TextViewDemo txtDemo = (TextViewDemo) findViewById(R.id.name);
Regards
Related
I am trying to draw a single line in Android using canvas
My class :
public class LineDrawer extends View {
public LineDrawer(Context context) {
super(context);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint();
paint.setColor(Color.BLACK);
paint.setStyle(Paint.Style.FILL_AND_STROKE);
paint.setStrokeWidth(10);
float left = 20;
float top = 20;
float right = 50;
float bottom = 100;
canvas.drawLine(left, top, right, bottom, paint);
}
}
My Main Activity :
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LineDrawer lineDrawer = new LineDrawer(this);
setContentView(R.layout.activity_Main);
}
}
I cannot find where is the problem , I try all the solutions in the internet but nothing happen , still a blank activity..
Should I import some code ?
lineDrawer is created but not added anywhere. Just creating a view is not enough, you need to add it to the current displayed views to be taken into account and drawn. You have two options:
Add it to your XML layout. You will have to add the following constructor to your custom view.
public LineDrawer(Context context, AttributeSet attrs) {
super(context, attrs);
}
Use addView(). Anyway, given how simple is your example, I'll use first (common) method.
As an additional comment, the Paint paint object should be created on view initialization, as is a costly operation. See in the original documentation for more information about this.
I created edit text with prefix with the help of this example.
Same way I tried to achieve suffix to edit text. I have successfully added suffix to the right side of my cursor.But it's not display at the very end of the Edit text. when I type on it, the typed text overlay on suffix. I know there are other way of achieving this but I'm trying to pull this off.Please help.
Basically I need to fulfill these functionalities.
Suffix should display at the end of the edit text.
Suffix should not be overlay from the text typed by the user.
here's my code.
public class SuffixEditText extends EditText {
private String mSuffix = "Suffix";
private Rect mSuffixRect = new Rect();
public SuffixEditText(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
getPaint().getTextBounds(mSuffix, 0, mSuffix.length(), mSuffixRect);
mSuffixRect.left += getPaint().measureText(" ");
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawText(mSuffix, super.getCompoundPaddingRight(), getBaseline(), getPaint());
}
#Override
public int getCompoundPaddingRight() {
return super.getCompoundPaddingRight()+ mSuffixRect.width();
}
Edit 1:
I tried Iharob Al Asimi's answer and I think it's good point to start. It's working but having following issues.
suffix not align with original text
Not getting original text color
I really like high quality results, it seems that on this platform it doesn't really matter for some programmers.
For me it's really important, that the look of my custom EditText view with a suffix is as natural as possible so I solved it in a very elegant and simple way.
Instead of doing some unreliable layout hacks or attempting to intercept the onDraw() method of the edit text, it would seem more natural to add a compound drawable to the right of the view and draw the text on it.
It's a simple solution, it doesn't break anything and it looks very "native".
From the example you linked, there is something that really helps a lot and it's the implementation of the onMesure() method, it makes room for the compound drawable, and that's precisely how I got the idea of using a compound drawable.
Here is the code so you can use it in future projects if the same requirement arises
public class SuffixEditText extends EditText {
private TextPaint mTextPaint;
private String mSuffix;
private float mSuffixWidth;
private Drawable mSuffixDrawable;
private void initialize(Context context) {
Resources resources = getResources();
mTextPaint = new TextPaint();
mTextPaint.setTextSize(getTextSize());
// Using the same foreground color could
// be confusing.
mTextPaint.setColor(getCurrentHintTextColor());
mTextPaint.setTextAlign(Paint.Align.RIGHT);
mSuffixDrawable = new Drawable() {
#Override
public void draw(#NonNull Canvas canvas) {
if (mSuffix == null)
return;
canvas.drawText(mSuffix, 0, getPaddingTop(), mTextPaint);
}
#Override
public void setAlpha(int alpha) {
}
#Override
public void setColorFilter(ColorFilter colorFilter) {
}
#Override
public int getOpacity() {
return PixelFormat.OPAQUE;
}
};
}
public SuffixEditText(Context context) {
super(context);
initialize(context);
}
public SuffixEditText(Context context, AttributeSet attrs) {
super(context, attrs);
initialize(context);
}
public SuffixEditText(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initialize(context);
}
public void setSuffix(String suffix) {
mSuffix = suffix;
setCompoundDrawables(null, null, mSuffixDrawable, null);
}
#Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (mSuffix != null)
mSuffixWidth = mTextPaint.measureText(" " + mSuffix);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
#Override
public int getCompoundPaddingRight() {
return super.getCompoundPaddingRight() + (int) Math.ceil(mSuffixWidth);
}
}
In my opinion, android's API is horribly designed and it makes doing something simple a very difficult task, when some other times it's incredibly stupid to do some other thing. I would prefer a consistent API like Qt's where you can do anything you want.
NOTE: There might happen to be some optimizations to this code but I am not a Java programmer, so I ignore the way things work and can't come up with more efficient code.
Also, a better implementation would take care of the other drawables in case they were present. Because setting them to null might override the previously set drawables anyway. And clearly, this view can't have an additional right drawable as it is implemented in this code.
I a using MpAndroidChart library. I need to implement a design where I need to color the area between two limit lines. I have attached an image for reference. I have tried multiple ways but I have failed to achieve it. I am using this library for the first time. Can anyone help me about how this could be achieved.
As you can see the green shade behind the line graph. Which is the limit. I need to get that green shade
Thanks in advance,
Anudeep Reddy.
I don't think that there is a direct way to achieve this, but this workaround should help you:
LimitLine ll = new LimitLine(lowerLimit, "Systolic range");
ll.setLineColor(Color.GREEN);
ll.setLineWidth(upperLimit - lowerLimit);
ll.setTextColor(Color.WHITE);
ll.setTextSize(12f);
chart.getAxisLeft().setDrawLimitLinesBehindData(true);
The important thing here is the method setDrawLimitLinesBehindData(true).
As always, all the information is available in the documentation.
I had the same problem but reached a different workaround without having to subclass the LineChart. Using canvas to draw the rectangle works, but you have to translate your charts coordinates to the canvas coordinates. You cannot use a single limit line as there is a limit to the width of the line. The workaround I used was to simply loop through limit lines to create a rectangle within my range.
float increment = (rangeHigh - rangeLow) / 20;
float metricLine = rangeLow;
for(int i = 0; i < 20; i++) {
LimitLine llRange = new LimitLine(metricLine, "");
llRange.setLineColor(Color.parseColor("#b5eb45"));
llRange.setLineWidth(10f);
leftAxis.addLimitLine(llRange);
metricLine = metricLine + increment;
}
As this is still an issue I throw in my two cents.
I tried the solution of #HouseOfHufflepuff but I got the error message that I use too much limit lines in the plot. It seems to work anyway but I guess the performance is not optimal.
So I implemented a subclass for drawing zones in the background. Maybe it's helpful for someone:
public class TargetZoneCombinedChart extends CombinedChart {
protected Paint mYAxisSafeZonePaint;
private List<TargetZone> mTargetZones;
public TargetZoneCombinedChart(Context context) {
super(context);
}
public TargetZoneCombinedChart(Context context, AttributeSet attrs) {
super(context, attrs);
}
public TargetZoneCombinedChart(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
protected void init() {
super.init();
mYAxisSafeZonePaint = new Paint();
mYAxisSafeZonePaint.setStyle(Paint.Style.FILL);
// mGridBackgroundPaint.setColor(Color.rgb(240, 240, 240));
mTargetZones = new ArrayList<>();
}
#Override
protected void onDraw(Canvas canvas) {
for (TargetZone targetZone : mTargetZones) {
// prepare coordinates
float[] pts = new float[4];
pts[1] = targetZone.lowerLimit;
pts[3] = targetZone.upperLimit;
mLeftAxisTransformer.pointValuesToPixel(pts);
// draw
mYAxisSafeZonePaint.setColor(targetZone.color);
canvas.drawRect(mViewPortHandler.contentLeft(), pts[1], mViewPortHandler.contentRight(),
pts[3], mYAxisSafeZonePaint);
}
super.onDraw(canvas);
}
public void addTargetZone(TargetZone targetZone){
mTargetZones.add(targetZone);
}
public List<TargetZone> getTargetZones(){
return mTargetZones;
}
public void clearTargetZones(){
mTargetZones = new ArrayList<>();
}
public static class TargetZone {
public final int color;
public final float lowerLimit;
public final float upperLimit;
public TargetZone(int color, float lowerLimit, float upperLimit) {
this.color = color;
this.lowerLimit = lowerLimit;
this.upperLimit = upperLimit;
}
}
}
To add a zone you just need to add a target zone object:
float rangeHigh = 180f;
float rangeLow = 80f;
chart.addTargetZone(new TargetZoneCombinedChart.TargetZone( Color.parseColor("#33b5eb45"),rangeLow,rangeHigh));
whereby the ranges are y values of the left axis.
This can be done by sub-classing the chart class (e.g. LineChart) and then overriding the onDraw() method. In the overridden onDraw() you can draw the rectangle(s) you need directly onto the canvas and then call super.onDraw() to complete the rendering of the chart.
There is an example of how to do this on the MP Android Github (see below). I followed the code in the example and it worked well for me.
https://github.com/PhilJay/MPAndroidChart/issues/485
I am trying to create a custom EditText class with a rectangle drawn around it. I got that to work rather easily. But then I also wanted the text to be centered and therefore I set gravity = center. However that made the rectangle around the box disappear. Is the gravity property changing the bounding box? How can I retrieve the correct bounding box?
The class is as follows:
public static class LetterBox extends EditText {
private Paint mPaint = new Paint();
public LetterBox(Context context) {
super(context);
this.setupPaint();
}
public LetterBox(Context context, AttributeSet attrs) {
super(context,attrs);
this.setupPaint();
super.setGravity(Gravity.CENTER);
}
public LetterBox(Context context, AttributeSet attrs, int defStyle) {
super(context,attrs,defStyle);
this.setupPaint();
super.setGravity(Gravity.CENTER);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawRect(0,0,getMeasuredWidth()-1,getMeasuredHeight()-1,mPaint);
super.onDraw(canvas);
}
private void setupPaint() {
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(Color.BLACK);
}
}
I have implemented your code and it is working as expected. The only thing I had to change was remove the static modifier from the class. Did you intend to use static?
I want to draw over the specific Line, Rectangle or Bitmap using Canvas. If i draw over the Bitmap, it will take the square shape empty background also.
So i want to draw over that particular Bitmap area only.
create a bitmap with "bmp1" name from your desire image
create a custom view
create a class and extend View like this
class MyCustomView extends View{
private Rect m_ImageRect;
private Rect m_TextRect ;
//you need these constructor
//you can init paint object or anything on them
public MyCustomView (Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
m_Context = context;
}
public MyCustomView (Context context, AttributeSet attrs)
{
super(context, attrs);
m_Context = context;
}
public MyCustomView (Context context)
{
super(context);
m_Context = context;
}
//then override on draw method
#Override
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
//here frist create two rectangle
//one for your image and two for text you want draw on it
m_ImageRect = canvas.getClipBounds();
m_TextRect = canvas.getClipBounds();
//it gives you an area that can draw on it,
//the width and height of your rect depend on your screen size device
canvas.drawBitmap(your bitmap(bmp1), null, m_ImageRect , paint);
canvas.save();
canvas.clipRect(m_TextRect);
canvas.drawText("your text", the x position you want to start draw,
the y position you want to start draw, m_paintText);
canvas.restore();
}
}
at the end put the custom view on your layout,and set field on it to send value to view for draw every thing you want
i hope it's help you,if this is not what you want!
post your code so maybe i can help you more
Seems like you need clipping. See exampls: http://www.example8.com/category/view/id/15543 , Understanding Android Canvas Clipping , http://jtomlinson.blogspot.com/2008/10/clipping.html
With clipping you can specify, which regions should be 'editable'.