How to initialize a custom component programmatically in Android? - java

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.

Related

How can I pass a float value to a class that extends LinearLayout before it gets inflated?

I created a class that extends LinearLayout to draw a border-radius and added the setRadius() function
public class RadiusLinearLayout extends LinearLayout {
private float CORNER_RADIUS;
private Bitmap maskBitmap;
private Paint paint, maskPaint;
private float cornerRadius;
public void setRadius(float cornerRadius) {
CORNER_RADIUS = cornerRadius;
}
public RadiusLinearLayout(Context context) {
super(context);
init(context, null, 0);
}
public RadiusLinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs, 0);
}
public RadiusLinearLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context, attrs, defStyle);
}
private void init(Context context, AttributeSet attrs, int defStyle) {
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
cornerRadius = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, CORNER_RADIUS, metrics);
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
maskPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
maskPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
setWillNotDraw(false);
}
#Override
public void draw(Canvas canvas) {
Bitmap offscreenBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
Canvas offscreenCanvas = new Canvas(offscreenBitmap);
super.draw(offscreenCanvas);
if (maskBitmap == null) {
maskBitmap = createMask(getWidth(), getHeight());
}
offscreenCanvas.drawBitmap(maskBitmap, 0f, 0f, maskPaint);
canvas.drawBitmap(offscreenBitmap, 0f, 0f, paint);
}
private Bitmap createMask(int width, int height) {
Bitmap mask = Bitmap.createBitmap(width, height, Bitmap.Config.ALPHA_8);
Canvas canvas = new Canvas(mask);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(Color.WHITE);
canvas.drawRect(0, 0, width, height, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
canvas.drawRoundRect(new RectF(0, 0, width, height), cornerRadius, cornerRadius, paint);
return mask;
}
}
On the fragment that corresponds to this class, OnCreateView I refence the Layout and set the radius value but it doesn't apply the value I send to the layout
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view: View = inflater.inflate(R.layout.calculator_fragment, container, false)
val test = view.findViewById(R.id.testCF) as RadiusLinearLayout
test.setRadius(2f)
return view
}
Is it possible to pass a value to the Layout before the fragment gets inflated?
Is it possible to pass a value to the Layout before the fragment gets inflated?
Basically yes (*), but I'd like to show how to change the radius of your custom View more flexibly:
There are certain changes to a View which make it necessary to update the UI, for example if you call View.setBackground().
In such cases, the View calls invalidate() to let the runtime know that it needs to be redrawn.
Your custom View can do so each time the radius is set:
public void setRadius(float cornerRadius) {
CORNER_RADIUS = cornerRadius;
// Now you need to calculate the field *cornerRadius* once more
//
DisplayMetrics metrics = getContext().getResources().getDisplayMetrics();
cornerRadius = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, CORNER_RADIUS, metrics);
invalidate();
}
By the way, setting the corner radius to 2f does not cause that much of a change - I tried with 24f, and it worked fine.
(*) If you absolutely need to change the radius before the custom View is inflated, you can either create it programmatically and call setRadius() before adding it to a ViewGroup.
Or you define a custom attribute to configure the View via the layout file.
Solution 1: It seems you have declared same name of a variable cornerRadius which is declared globally and parameter of setradius method. It created ambiguity. Change the parameter name and do a try.
Something like this
private float cornerRadius;
public void setRadius(float radius) {
CORNER_RADIUS = radius;
}
Solution 2: Pass corner radius value by its constructor
Example:
public RadiusLinearLayout(Context context,float radius) {
super(context);
CORNER_RADIUS = radius;
init(context, null, 0);
}

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.

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;
}
}

Missing value when pass to another class - Android - Java?

I am passing a value to another class, I get the value in another class, but when I am using the value, it shows me 0.
public class MaterialSpinner extends Spinner implements ValueAnimator.AnimatorUpdateListener {
public int widths;
public MaterialSpinner(Context context, int width) {
super(context);
this.widths = width;
// **HERE SHOW ME VALUE FOR EXAMPLE 596**
Log.i("LOG-1", String.valueOf(widths));
init(context, null);
}
#Override
protected void onDraw(Canvas canvas) {
//BUT HERE GET ME 0**
int startX = this.widths;
Log.i("LOG-2", String.valueOf(widths));
}
}
I have other constructors:
public MaterialSpinner(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public MaterialSpinner(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
Here is my Activity; I pass the value from here to the MaterialSpinner class:
public class StartActivity extends AppCompatActivity {
Context context;
private ArrayAdapter<String> adapter;
int width;
int height;
MaterialSpinner spinner1;
private boolean shown = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_start);
context = this;
if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB_MR2) {
Display display = getWindowManager().getDefaultDisplay();
width = display.getWidth();
height = display.getHeight();
}
else {
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
width = size.x;
height = size.y;
}
new MaterialSpinner(context, width);
adapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, ITEMS);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
initSpinnerHintAndFloatingLabel();
}
private void initSpinnerHintAndFloatingLabel() {
spinner1 = (MaterialSpinner) findViewById(R.id.spinner1);
spinner1.setAdapter(adapter);
spinner1.setPaddingSafe(0, 0, 0, 0);
}
}
you are creating an object, you do nothing with it:
new MaterialSpinner(context,width);
in the other function you do this:
spinner1 = (MaterialSpinner) findViewById(R.id.spinner1);
this will inflate the object from the activity_start layout. The easies way would be to create a setWidth() function and call it after you inflated the View
spinner1 = (MaterialSpinner) findViewById(R.id.spinner1);
spinner1.setWidth(width);
EDIT: and remove new MaterialSpinner(context,width); since it does nothing

Is this a good method to make ImageView square?

Parent layout has a specified width value (50dp), and height set to MATCH_PARENT. In that layout I am adding a few ImageView widgets using Java, and because parent layout has specified width, I can set width and height of each ImageView to MATCH_PARENT and using Java I can make that ImageView square by setting its height as width.
This is my code:
public class Icon extends ImageView {
public AppIcon(final Context context)
{
super(context);
}
public AppIcon(final Context context, final AttributeSet attrs)
{
super(context, attrs);
}
public AppIcon(final Context context, final AttributeSet attrs, final int defStyle)
{
super(context, attrs, defStyle);
}
#Override
protected void onMeasure(final int width, final int height)
{
setMeasuredDimension(width, width);
}
}
Is this a good method to make each ImageView square? I think it's too simple, like something is missing.
P.S.: My method works, but I need to be sure, that everything is fine.
Your code is working one but it can be enhanced. See my code:
Updated my code.
The image will be scaled based on the measured width and height and whichever is smaller.
public class Icon extends ImageView {
public Icon(final Context context) {
super(context);
}
public Icon(final Context context, final AttributeSet attrs) {
super(context, attrs);
}
public Icon(final Context context, final AttributeSet attrs,
final int defStyle) {
super(context, attrs, defStyle);
}
#Override
protected void onMeasure(int width, int height) {
super.onMeasure(width, height);
int measuredWidth = getMeasuredWidth();
int measuredHeight = getMeasuredHeight();
if (measuredWidth > measuredHeight) {
setMeasuredDimension(measuredHeight, measuredHeight);
} else {
setMeasuredDimension(measuredWidth, measuredWidth);
}
}
}

Categories