Different behaviour from custom textview on different android api levels - java

I have a problem with a custom text view component in android. I use a custom spannable to set the background of my textview as seen in the first image. This works properly on android api level 21 (first image). But if I use api level 25 (second image) the custom textview disappears.
Code from my custom text view
public class MetalTextView extends android.support.v7.widget.AppCompatTextView
{
private final String xmlns = "http://schemas.android.com/apk/res/android";
private Context context;
private AttributeSet attributeSet;
private String value;
public MetalTextView(Context context)
{
super(context);
this.context = context;
value = attributeSet.getAttributeValue(xmlns, "text");
}
public MetalTextView(Context context, #Nullable AttributeSet attrs)
{
super(context, attrs);
this.context = context;
this.attributeSet = attrs;
value = attributeSet.getAttributeValue(xmlns, "text");
}
public MetalTextView(Context context, AttributeSet attrs, int defStyleAttr)
{
super(context, attrs, defStyleAttr);
this.context = context;
this.attributeSet = attrs;
value = attributeSet.getAttributeValue(xmlns, "text");
}
#Override
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
final Spannable spannable = new SpannableString(value);
spannable.setSpan(new RoundedBackgroundSpannable(), 0, value.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
this.setText(spannable);
}
#Override
public void setText(CharSequence text, BufferType type)
{
super.setText(text, type);
value = text.toString();
final Spannable spannable = new SpannableString(text);
spannable.setSpan(new RoundedBackgroundSpannable(), 0, value.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
Code from my custon spannable
public class RoundedBackgroundSpannable extends ReplacementSpan
{
private static int CORNER_RADIUS = 20;
private final int backgroundColor = 0xFFFFFFFF;
private final int textColor = 0xFF000000;
private final int borderColor = 0xFF000000;
private final float strokeWidth = 5f;
public RoundedBackgroundSpannable()
{
super();
}
#Override
public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint)
{
final RectF rectF = new RectF(x, top, x + measureText(paint, text, start, end ), bottom);
paint.setColor(backgroundColor);
canvas.drawRoundRect(rectF, CORNER_RADIUS, CORNER_RADIUS, paint);
paint.setColor(textColor);
canvas.drawText(text, start, end, x, y, paint);
paint.setColor(borderColor);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(strokeWidth);
final Path path = new Path();
path.addRoundRect(rectF, CORNER_RADIUS, CORNER_RADIUS, Path.Direction.CW);
canvas.drawPath(path, paint);
}
#Override
public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm)
{
return Math.round(paint.measureText(text, start, end));
}
private float measureText(Paint paint, CharSequence text, int start, int end)
{
return paint.measureText(text, start, end);
}
}
My usage of my custom component
<com.metalcrunch.ui.MetalTextView
android:id="#+id/metalTextView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="#dimen/textview_headline_margin_top"
android:layout_weight="0.1"
android:gravity="center"
android:text="Some Text"
android:textSize="#dimen/textview_headline_text_size"/>
What is the reason that the textview disappears on api level 25?

Related

How to animate the level meter bar?

I am fairly new to Java and android studio. I would like to animate my bar which I created with random value. I have created customView with android studio and drawn a canvas rectangle. Next step is feed some random values and animate the bar. I acheived this feat with (Math.random) in JavaScript.
public class CustomView extends View {
private Rect rect;
private Paint paint;
int radius;
public CustomView(Context context){
super(context);
init(null);
}
public CustomView(Context context, AttributeSet attrs){
super(context, attrs);
init(attrs);
}
public CustomView(Context context, AttributeSet attrs, int defStyleAttr){
super(context, attrs, defStyleAttr);
init(attrs);
}
public CustomView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){
super(context, attrs, defStyleAttr, defStyleRes);
init(attrs);
}
private void init(#Nullable AttributeSet set){
rect = new Rect();
// the flag is more pixelated
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
postInvalidate();
}
//drawing a canvas
#Override
protected void onDraw(Canvas canvas){
int viewWidth = getWidth();
int viewHeight = getHeight();
int leftTopX = viewWidth - 750;
int leftTopY = viewHeight - 750;
int rightBotX = viewWidth + 150;
int rightBotY = viewHeight + 150;
paint.setColor(Color.MAGENTA);
canvas.drawRect(leftTopX,leftTopY, rightBotX,rightBotY,paint);
paint.setColor(Color.GREEN);
}
}
This is what I have achieved so far by now.But how can I animate bar with Java ?
Try this -
I used it with seekbar. You can use it for your view. As per your case. You should Progressbar or Seekbar.
seekbar = ((CustomSeekbar) findViewById(R.id.customSeekBar));
seekbar.setMax(2600);
final ObjectAnimator animation = ObjectAnimator.ofInt(seekbar, "progress", 0, 800;
animation.setDuration(1500);
animation.setInterpolator(new DecelerateInterpolator());
animation.start();
seekbar.clearAnimation();
Here 0 is the starting value and 800 is the max value. Please change is as per your requirement.

How to initialize a custom component programmatically in Android?

I already know how to use a custom component in a xml. Now, I'm trying to call one programmatically. I haven't found any tutorial about this so I tried to do it like how we declare a view programmatically. One problem I stumbled upon is that how do I get an AttributeSet that I pass on my constructor? I don't know how I get or create that to pass it for my custom component.
This is my custom component class.
public class CheckOutItem extends ConstraintLayout {
public Context ctx;
public Paint mPaint;
public Rect mRect;
int mSquareColor;
public ImageView imgThumbnail;
public TextView lblAbbrev, lblFullName;
public ConstraintLayout lytMain;
String TAG = "sharePOS";
public CheckOutItem(Context context) {
super(context);
ctx = context;
init(null);
}
public CheckOutItem(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
LayoutInflater.from(context).inflate(R.layout.checkout_item, this);
ctx = context;
init(attrs);
}
public CheckOutItem(Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
ctx = context;
init(attrs);
}
private void init(#Nullable AttributeSet set){
//inflate(ctx, R.layout.checkout_item, this);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mRect = new Rect();
if(set == null){
return;
}
TypedArray ta = getContext().obtainStyledAttributes(set, R.styleable.CheckOutItem);
this.imgThumbnail = findViewById(R.id.imgItemThumbnail);
this.lblAbbrev = findViewById(R.id.lblItemAbbrevName);
this.lblFullName = findViewById(R.id.lblItemFullName);
this.lytMain = findViewById(R.id.lytMain);
this.lblAbbrev.setText(ta.getText(R.styleable.CheckOutItem_abbrevText));
this.lblAbbrev.setTextSize(ta.getDimension(R.styleable.CheckOutItem_abbrevTextSize, 1f));
this.lblAbbrev.setTextColor(ta.getColor(R.styleable.CheckOutItem_abbrevTextColor, Color.BLACK));
this.lblFullName.setText(ta.getText(R.styleable.CheckOutItem_fullNameText));
this.lblFullName.setTextSize(ta.getDimension(R.styleable.CheckOutItem_fullNameTextSize, 1f));
this.lblFullName.setTextColor(ta.getColor(R.styleable.CheckOutItem_fullNameTextColor, Color.BLACK));
this.lblFullName.setBackgroundColor(ta.getColor(R.styleable.CheckOutItem_fullNameBackgroundColor, Color.WHITE));
this.lytMain.setBackgroundColor(ta.getColor(R.styleable.CheckOutItem_mainBackgroundColor, Color.LTGRAY));
this.lytMain.setBackground(ta.getDrawable(R.styleable.CheckOutItem_mainBackgroundDrawable));
ta.recycle();
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mRect.left = 0;
mRect.right = getWidth();
mRect.top = 0;
mRect.bottom = getHeight();
Log.d(TAG, "rect: " + mRect);
canvas.drawRect(mRect, mPaint);
}
}
UPDATE:
How do I set a margin in the custom component? Regular LayoutParams with setMargins() doesn't seem to work.

Drawing StaticLayout not working correct

I've implemented a View with a Text.Layout and I'm drawing it but I'm getting nothing. Here is my code
public class MyEfficientTextView extends View {
Layout textLayout;
SpannableStringBuilder spannableStringBuilder;
int width = 0, height = 0;
TextPaint textPaint;
CharSequence text;
public MyEfficientTextView(Context context) {
this(context, null, 0);
}
public MyEfficientTextView(Context context, #Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public MyEfficientTextView(Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
spannableStringBuilder = new SpannableStringBuilder();
}
public void setText(CharSequence charSequence) {
text = charSequence;
spannableStringBuilder.clear();
spannableStringBuilder.append(text);
textPaint = new TextPaint();
textPaint.setAntiAlias(true);
textPaint.setTextSize(16 * getResources().getDisplayMetrics().density);
textPaint.setColor(getResources().getColor(R.color.textColor));
textLayout = new StaticLayout(spannableStringBuilder, textPaint, width, Layout.Alignment.ALIGN_NORMAL, 1, 0, false);
invalidate();
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if (w != width) {
width = w;
setText(text);
}
height = h;
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
if (textLayout != null) {
canvas.translate(getPaddingLeft(), getPaddingTop());
textLayout.draw(canvas);
}
canvas.restore();
}
}
this code is just for testing if it's working or not and it's not efficient. Which part is wrong and causing it to not render text?
you should override onMeasure and set the view width/height. your example code will result in default width/height (probably 0).

Custom Circular TextView - Text Not Centered in Circle

I put together a TextView class utilizing some different suggestions I've seen and wrote this class to display a TextView inside of a circle. The circle comes out great, but the text appears slightly above the center of the circle.
I can't figure out what's causing this. Here's my code:
CircularTextView
public class CircularTextView extends AppCompatTextView {
private ShapeDrawable backgroundDrawable;
private OvalShape ovalShape;
private int backgroundColor;
public CircularTextView(Context context) {
super(context);
backgroundColor = ContextCompat.getColor(context, R.color.color_circle_test_solid);
allocateShapes();
}
public CircularTextView(Context context, AttributeSet attrs) {
super(context, attrs);
backgroundColor = ContextCompat.getColor(context, R.color.color_circle_test_solid);
allocateShapes();
}
public CircularTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
backgroundColor = ContextCompat.getColor(context, R.color.color_circle_test_solid);
allocateShapes();
}
//Source https://stackoverflow.com/questions/25203501/android-creating-a-circular-textview/34685568#34685568
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int h = this.getMeasuredHeight();
int w = this.getMeasuredWidth();
int r = Math.max(w, h);
setMeasuredDimension(r, r);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
backgroundDrawable.setShape(ovalShape);
backgroundDrawable.getPaint().setColor(backgroundColor);
setBackground(backgroundDrawable);
}
private void allocateShapes(){
backgroundDrawable = new ShapeDrawable();
ovalShape = new OvalShape();
}
public void setBackgroundColor(int color){
backgroundColor = color;
invalidate();
}
}
TestCircleTextViewActivity
public final class TestCircleTextViewActivity extends BaseActivity {
#BindView(R.id.circle_text)
CircularTextView circleText;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test_circular_textview);
ButterKnife.bind(this);
int circleColor = ContextCompat.getColor(this, R.color.color_circle_test_solid);
circleText.setBackgroundColor(circleColor);
}
}
activity_test_circular_textview.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.thinkbubble.app.ui.view.CircularTextView
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:id="#+id/circle_text"
android:layout_gravity="center"
android:layout_centerInParent="true"
android:padding="4dp"
android:text="Keyword">
</com.thinkbubble.app.ui.view.CircularTextView>
</RelativeLayout>
Use android:gravity="center" for you TextView to make text center in circle

Using onClick() defined in separate class in ViewHolder

I have a RecyclerView holding CardViews with a favorite button within each card. I would like the favorite button to only be clicked for each specific card. I am currently using a ViewHolder to maintain each of the components in each card and the favorite button is one of those components.
How can I use an onClick() defined in a separate class within the ViewHolder?
Using this code currently only actives the onTouchEvent() for the favoriteButton
viewHolder.favoriteButton.setOnClickListener(new LikeButtonView(mContext));
AppAdapter.java
public class AppAdapter extends RecyclerView.Adapter<AppAdapter.ViewHolder> {
PackageManager packageManager;
private List<App> apps;
private int rowLayout;
private Context mContext;
public AppAdapter(List<App> apps, int rowLayout, Context context) {
this.apps = apps;
this.rowLayout = rowLayout;
this.mContext = context;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
packageManager = this.mContext.getPackageManager();
View v = LayoutInflater.from(viewGroup.getContext()).inflate(rowLayout, viewGroup, false);
return new ViewHolder(v);
}
#Override
public void onBindViewHolder(ViewHolder viewHolder, int i) {
App appObject = apps.get(i);
viewHolder.appName.setText(appObject.getApplicationName());
viewHolder.versionNumber.setText(String.valueOf(appObject.getVersionNumber()));
viewHolder.updateDate.setText(String.valueOf(appObject.getLastUdpateTime()));
viewHolder.appIcon.setImageDrawable(appObject.getAppIcon());
viewHolder.appChangelog.setText(appObject.getChangelogText());
viewHolder.favoriteButton.setOnClickListener(new LikeButtonView(mContext));
}
LikeButtonView.java
public class LikeButtonView extends FrameLayout implements View.OnClickListener {
private static final DecelerateInterpolator DECCELERATE_INTERPOLATOR = new DecelerateInterpolator();
private static final AccelerateDecelerateInterpolator ACCELERATE_DECELERATE_INTERPOLATOR = new AccelerateDecelerateInterpolator();
private static final OvershootInterpolator OVERSHOOT_INTERPOLATOR = new OvershootInterpolator(4);
#Bind(R.id.ivStar)
ImageView ivStar;
private boolean isChecked;
private AnimatorSet animatorSet;
public LikeButtonView(Context context) {
super(context);
init();
}
public LikeButtonView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public LikeButtonView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public LikeButtonView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init();
}
private void init() {
LayoutInflater.from(getContext()).inflate(R.layout.view_like_button, this, true);
ButterKnife.bind(this);
setOnClickListener(this);
}
#Override
public void onClick(View v) {
isChecked = !isChecked;
ivStar.setImageResource(isChecked ? R.drawable.ic_star_rate_on : R.drawable.ic_star_rate_off);
if (animatorSet != null) {
animatorSet.cancel();
}
if (isChecked) {
ivStar.animate().cancel();
ivStar.setScaleX(0);
ivStar.setScaleY(0);
animatorSet = new AnimatorSet();
ObjectAnimator starScaleYAnimator = ObjectAnimator.ofFloat(ivStar, ImageView.SCALE_Y, 0.2f, 1f);
starScaleYAnimator.setDuration(350);
starScaleYAnimator.setStartDelay(0);
starScaleYAnimator.setInterpolator(OVERSHOOT_INTERPOLATOR);
ObjectAnimator starScaleXAnimator = ObjectAnimator.ofFloat(ivStar, ImageView.SCALE_X, 0.2f, 1f);
starScaleXAnimator.setDuration(350);
starScaleXAnimator.setStartDelay(0);
starScaleXAnimator.setInterpolator(OVERSHOOT_INTERPOLATOR);
animatorSet.playTogether(
starScaleYAnimator,
starScaleXAnimator
);
animatorSet.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationCancel(Animator animation) {
ivStar.setScaleX(1);
ivStar.setScaleY(1);
}
});
animatorSet.start();
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
ivStar.animate().scaleX(0.7f).scaleY(0.7f).setDuration(150).setInterpolator(DECCELERATE_INTERPOLATOR);
setPressed(true);
break;
case MotionEvent.ACTION_MOVE:
float x = event.getX();
float y = event.getY();
boolean isInside = (x > 0 && x < getWidth() && y > 0 && y < getHeight());
if (isPressed() != isInside) {
setPressed(isInside);
}
break;
case MotionEvent.ACTION_UP:
ivStar.animate().scaleX(1).scaleY(1).setInterpolator(DECCELERATE_INTERPOLATOR);
if (isPressed()) {
performClick();
setPressed(false);
}
break;
}
return true;
}
}

Categories