I'm trying to create a custom TextView. I look here for my guidance as well and I made it working, though I'm still confused by why the other component is not being drawn, this is just more simple than the reference guide of android but it's not working in mine.
From the custom class I made (CustomTextView w/c extends TextView),
I try to add a OnTouchListener() and invoke the invalidate() method for both this and other component w/c is a TextView but it still not being drawn.
CustomTextView.java
public class CustomTextView extends TextView {
TextView extra_detail;
public CustomTextView(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CustomTextView);
extra_detail = new TextView(context);
extra_detail.setText("Hey!");
extra_detail.setPadding(20, 0, 0, 0);
extra_detail.setTextSize(2);
a.recycle();
//I also tried this in case the space isn't enough
setMinHeight(80);
extra_detail.invalidate();
this.invalidate();
}
final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//Here's my problem
//both are not being drawn
//even if I remove the other one and vice-versa
canvas.drawText("Hey", 10, 0, paint);
extra_detail.draw(canvas); //is this the right way to do this?
}
#Override
public boolean onTouchEvent(MotionEvent event) {
invalidate();
extra_detail.invalidate();
//still not being drawn
//after the invalidation
Toast.makeText(getContext(), "Hey!", Toast.LENGTH_SHORT).show();
return super.onTouchEvent(event);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
extra_detail.measure(widthMeasureSpec, heightMeasureSpec);
}
}
It seems I'm missing something here but cannot figure it out.
Update
I happen to extend my Activity with ActionBarActivity. Now with this kind of pre defined Activity, there is a title bar implemented and its hiding all the canvas.draw*() I made on the top-left-most of the screen. I figure it out after I invoke the setY(10) then the canvas.drawText() appears.
But I still have the problem of drawing another View in onDraw() event.
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.
How to set button height less then 30px? I tried the next:
setHeight(30); (set 150 is working, but set 30 is not)
setPadding(0,-20,0,-20); (width is changes, height is not)
LinearLayout.LayoutParams (set 150 is working, but set 30 is not)
new Button(this, null, android.R.attr.buttonStyleSmall); - effects only on button text
Button mainButton = new Button(this);
mainButton.setPadding(0, 0, 0, 0);
mainButton.setLayoutParams(new LayoutParams(FlowLayout.LayoutParams.WRAP_CONTENT,FlowLayout.LayoutParams.WRAP_CONTENT));
l.addView(mainButton);
SmallButton mainButton = new SmallButton(this);
mainButton.setText(s);
mainButton.setBackgroundResource(R.drawable.button_drawable);
mainButton.setLayoutParams(new LayoutParams(FlowLayout.LayoutParams.WRAP_CONTENT,FlowLayout.LayoutParams.WRAP_CONTENT));
l.addView(mainButton);
public class SmallButton extends Button {
public SmallButton(Context context) {
super(context);
}
public SmallButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SmallButton(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec,MeasureSpec.makeMeasureSpec(MeasureSpec.EXACTLY, 35));
}
#Override
protected void onDraw(Canvas canvas) {
canvas.save();
canvas.translate(0,-5);
super.onDraw(canvas);
canvas.restore();
}
}
I have a Shape drawable with gradient for my background as I don't know the exact width. I also tried to setup its height. Is it possible to make button wrap it text only?
EDIT:
The problem is solved with the use of TextView instead of the Button. Ofcourse if you are not using the specific/overriden things in Button.class.
There are two parameters.
Button.setMinimumHeight() and Button.setMinHeight(). Ensure that both of them are set to value "0".
Every View in Android can have a minimum width and height set for them. A Button will set these depending on the style it is using on the device it is using.
However, this can be overridden in the xml for the button:
android:minHeight="0dip"
android:minWidth="0dip"
As #Sudar Nimalan points out, this is related to the background of the button. If you don't wish to create your own button class, you can simply set the background to something smaller. Try setting the background to a solid colour, or create your own background for it.
try this
mainButton.setIncludeFontPadding(false);
I think, This causes due the default background drawable minimum height size, you can sent a empty background or color and test these scenarios.
If you want to change the height with default drawable, you can create your Custom Button extending the Button class and override on onMeasure and onDraw etc.
override the onDraw to shift the default button drawing:
onDraw(canvas){
canvas.save();
canvas.translate(0,value to shift);
super.onDraw(canves);
canvas.restore();
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec,MeasureSpec.makeMeasureSpec(MeasureSpec.EXACTLY, 35));
}
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
I'm creating a custom widget by extending LinearLayout:
public class MyWidget extends LinearLayout {
private static Paint PAINT = new Paint(Paint.ANTI_ALIAS_FLAG);
static {
PAINT.setColor(Color.RED);
}
public MyWidget(Context context) {
this(context, null);
}
public MyWidget(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawCircle(canvas.getWidth() / 2, canvas.getHeight()/2, canvas.getWidth()/2, PAINT);
// never gets called :-(
}
#Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
// this gets called, but with a canvas sized after the padding.
}
}
I can add children just fine, but I'm never getting my custom onDraw() being called. dispatchDraw() gets called, but that seems to have a different canvas (the one that's within the padding. I need to draw on the whole layout area). Is there some flag that needs to get set to get onDraw() called for the layout?
You need to call setWillNotDraw(false) in your constructor.
Because by default a layout does not need to draw, so an optimization is to not call is draw method. By calling setWillNotDraw(false) you tell the UI toolkit that you want to draw.