How to center setLogo drawable programatically in toolbar - java

I have a custom toolbar that's built without a xml, all the code is just written in java. I am using the setLogo() method to add a drawable but the image's default gravity is to the left. How can I change its position/gravity so that it's in the middle?
I was trying to override the setLogo() method but I can't apply a LayoutParams to a drawable.
This is what I am talking about:
#Override
public void setLogo(#DrawableRes int resId) {
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
float factor = getContext().getResources().getDisplayMetrics().density;
int margin = (int) (16 * factor);
lp.setMargins(margin, 0, margin, 0);
lp.gravity = Gravity.CENTER;
resId.setLayoutParams(lp); // this does not work
setLogo(AppCompatResources.getDrawable(getContext(), resId));
}
What's the right way to achieve this without a xml to refer to?
Thanks!

Related

RelativeLayout LayoutParams incorrect scaling

I have a Relative Layout within an XML file with an ImageView element which contains both width and height for an image.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="#+id/tankHeight"
android:layout_width="210dp"
android:layout_height="350dp"
android:layout_centerInParent="true"
app:srcCompat="#drawable/tank_progress" />
I then try and dynamically change the height (only height, not width) of the image when the method is called in its class, as follows...
public void updateTank() {
ImageView myTank = (ImageView)findViewById(R.id.tankHeight);
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams)
myTank.getLayoutParams();
params.height = 350 - 35;
myTank.setLayoutParams(params);
}
However, when this method is called, the image appears to scale by the specified amount in both height and width, when I only want to reduce the height by 35 pixels. My understanding is that this may be due to using a relative layout, but I am not sure how to overcome the issue, so that I can programmatically change the height only. From reading this and this, I had thought the method was set up correctly, so am unsure why it is displaying differently than intended?
The default ScaleType for an ImageView is FIT_CENTER. What this means is that the source image will be scaled so that you can see the entire thing, regardless of the view's aspect ratio... so even though your ImageView is probably only changing its height, the image content is being scaled in both dimensions despite only having the view height change.
Try setting android:scaleType="centerCrop" on your ImageView tag. This will allow you to resize the view without re-scaling the image contents.
More info on scale types: https://robots.thoughtbot.com/android-imageview-scaletype-a-visual-guide
Changing a view height and/or width requires it to be redrawn, try calling requestLayout(). Hope it helps.
public void updateTank() {
ImageView myTank = (ImageView)findViewById(R.id.tankHeight);
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams)
myTank.getLayoutParams();
params.height = 350 - 35;
myTank.setLayoutParams(params);
myTank.requestLayout();
}
I believe you should first convert dp to pixel.
public int convertDpToPixel(float dp) {
Context context = getContext();
if (context == null) {
return 0; // context should never be a null
}
Resources resources = context.getResources();
DisplayMetrics metrics = resources.getDisplayMetrics();
return (int) (dp * ((float) metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT));
}
and then you can call
myTank.getLayoutParams();
params.height = convertDpToPixel(350) - convertDpToPixel(35);
myTank.setLayoutParams(params);

ConstraintLayout Guideline Percent Animation

I'm fairly new on using ConstraintLayout (java).
What I want to achieve is something like when the numpad is being shown/hidden as a slide animation, I've tried something like this:
Animation a = new Animation() {
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
ConstraintLayout.LayoutParams lparams = (ConstraintLayout.LayoutParams) guideline.getLayoutParams();
lparams.guidePercent = 0.5f;
guideline.setLayoutParams(lparams);
}
};
a.setDuration(3000);
a.setFillAfter(true);
guideline.startAnimation(a);
Yes, the guideline (and corresponding views that is attached to it) is being moved to the center of the screen but it is not as smooth as it is supposed to be. Is there a way to achieve the smoothness of the animation?
Any suggestion would be much appreciated!
You can use a ValueAnimator
Sample in Kotlin.
val guideline = findViewById<View>(R.id.guideline2) as Guideline
val end = (guideline.layoutParams as ConstraintLayout.LayoutParams).guidePercent
// get end percent. start at 0
val valueAnimator = ValueAnimator.ofFloat(0f, end)
valueAnimator.duration = 3000
// set duration
valueAnimator.interpolator = AccelerateDecelerateInterpolator()
// set interpolator and updateListener to get the animated value
valueAnimator.addUpdateListener { valueAnimator ->
val lp = guideline.layoutParams as ConstraintLayout.LayoutParams
// get the float value
lp.guidePercent = valueAnimator.animatedValue as Float
// update layout params
guideline.layoutParams = lp
}
valueAnimator.start()
Java Version
final Guideline guideline = findViewById(R.id.guideline2) ;
ConstraintLayout.LayoutParams lp = (ConstraintLayout.LayoutParams)guideline.getLayoutParams();
float end =lp.guidePercent;
ValueAnimator valueAnimator = ValueAnimator.ofFloat(0f, end);
valueAnimator.setDuration(3000);
valueAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
ConstraintLayout.LayoutParams lp = (ConstraintLayout.LayoutParams)guideline.getLayoutParams();
lp.guidePercent = valueAnimator.getAnimatedFraction();
guideline.setLayoutParams(lp);
}
});
Try animating your layout with ConstraintSet. See Beautiful animations using Android ConstraintLayout.
[T]here is one other benefit of ConstraintLayout that most people are unaware of and the official documentation curiously doesn’t mention anything about: performing cool animations on your ConstraintLayout views with very little code.
There are other sources and some videos on this technique. I think that you will find it a smoother way to do what you are trying to do.

Get what the WRAP_CONTENT height would be

My purpose is to have an invisible LinearLayout that appear whit an animation when click on a specific button. To do this i have set the default height to WRAP_CONTENT, get the height when the app start, set the height to 0 and start the animation when i click on the button. Here is the code:
linearLayout.post(new Runnable() {
#Override
public void run(){
height = linearLayout.getMeasuredHeight();
linearLayout.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, 0));
}
});
findViewById(R.id.btnOperator).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Animation ani = new ShowAnim(linearLayout, height/* target layout height */);
ani.setDuration(1000/* animation time */);
linearLayout.startAnimation(ani);
}
});
This work pretty good, but i want to do different. I want that the default height is 0, and then calculate what the WRAP_CONTENT height would be, and pass it to:
Animation ani = new ShowAnim(linearLayout, height/* target layout height */);
How could i do this? I searched but found anything.
Try this code:
linearLayout.measure(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
height = linearLayout.getMeasuredHeight();

how to change a background color when the soft keyboard is out and change it back when it's in

I'm writing an android view (Android 12).
I have a linearlayout with editText controls on it.
I want to change the linearlayout background image when the soft keyboard is out and change it again when the keyboard is hidden.
I have tried to set a focus listener on each editText, but it won't help.
How can I achieve this?
First, add an id to your layout:
android:id="#+id/view"
So for example:
<LinearLayout
android:id="#+id/view"
android:layout_width="match_parent"
android:layout_height="match_parent" >
Then use this code from this question to determine if the soft keyboard is visible. You should probably put this in your onCreate method.
final View root = findViewById(R.id.view);
root.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
int heightDiff = root.getRootView().getHeight() - root.getHeight();
if (heightDiff > 100) { // more than 100 pixels is probably a keyboard
// keyboard is shown
layout.setBackground(getResources().getDrawable(R.drawable.idOfPic));
} else {
// keyboard is not shown
layout.setBackground(getResources().getDrawable(R.drawable.otherPic));
}
}
});
Note depending on your layout (speaking from my own experience), the if (heightDiff > 100) may have to change. It might be if (heightDiff > 150) or something else; the pixel height is arbitrary.
Unfortunately, there is no real way to determine if the soft keyboard is visible (ridiculous). This is the best way it can be done.
try this:
final View activityRootView = findViewById(R.id.activityRoot);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
Rect r = new Rect();
//r will be populated with the coordinates of your view that area still visible.
activityRootView.getWindowVisibleDisplayFrame(r);
int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
heightDiff = convertPixelsToDp(heightDiff , this);
if (heightDiff > 100) { // if more than 100 pixels, its probably a keyboard...
... do something here
}
}
});
more info in this link and this
for working in all device change heightDiff to dp, and work with that and for changing that use following method:
public static float convertPixelsToDp(float px, Context context){
Resources resources = context.getResources();
DisplayMetrics metrics = resources.getDisplayMetrics();
float dp = px / (metrics.densityDpi / 160f);
return dp;
}

Android ViewGroup.setScaleX() cause the view to be clipped

I use NineOldAndroids library to scale my custom layout.
public class MyLayout extends FrameLayout {
// LayoutParams.MATCH_PARENT and all.
...
#Override
public boolean setPositionAndScale(ViewGroup v, PositionAndScale pas, PointInfo pi) {
...
mScale = pas.getScale();
ViewHelper.setScaleX(this, mScale);
ViewHelper.setScaleY(this, mScale);
}
}
I have tried FrameLayout and AbsoluteLayout. All have the same effect.
When mScale < 1.0 scaling/zooming works but part of the layout is clipped.
mScale = 1.0:
mScale < 1.0: scaling/zooming works but layout is clipped
How can i fix this issue?
Edit: The picture was taken on ICS. So I don't think it's NineOldAndroids problem.
The parent of your view must have the property android:clipChildren disabled (from layout file or with setClipChildren(false) ).
But with this method you don't get the touch events outside the view clip bounds. You can work around by sending them from your activity or writing a custom ViewGroup parent.
I'm using a different hack which seems to work in my case, the trick is to maintain your own transformation matrix. Then, you have to overload a lot of ViewGroup's method to make it work. For example :
#Override
protected void dispatchDraw(Canvas canvas) {
Log.d(TAG, "dispatchDraw " + canvas);
canvas.save();
canvas.concat(mMatrix);
super.dispatchDraw(canvas);
canvas.restore();
}
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.d(TAG, "dispatchTouchEvent " + ev);
ev.transform(getInvMatrix()); //
return super.dispatchTouchEvent(ev);
}
private Matrix getInvMatrix()
{
if(!mTmpMatIsInvMat)
mMatrix.invert(mTmpMat);
mTmpMatIsInvMat = true;
return mTmpMat;
}
In case anyone got in to the same situation as me.
I ended up using this approach:
protected void setScale(float scale, boolean updateView) {
mScale = scale;
if (updateView) {
LayoutParams params = getLayoutParams();
onUpdateScale(scale, params);
setLayoutParams(params);
}
}
protected void onUpdateScale(float scale, LayoutParams params) {
params.leftMargin = (int) (mModel.getX() * scale);
params.topMargin = (int) (mModel.getY() * scale);
params.width = (int) (mModel.getWidth() * scale);
params.height = (int) (mModel.getHeight() * scale);
}
Since API Level 11, the View class has setScaleX() and setScaleY() methods, that work as expected and also scale sub-views of the scaled view. So, if that'd be a way for you, drop the library and just do
v.setScaleX(mScale);
v.setScaleY(mScale);
If I understand your problem correctly, you are scaling a view group and expect the included views to scale accordingly. It doesn't work that way: you scale the view group and it changes size, but its children views do not.
Just scale all subviews. Even so, I am not sure that texts and images are going to be automatically scaled. What you want is zoom, not scale. Try this reference.
Use ViewGroup.layout. It may be the easiest way to scale(&move) ViewGroup.

Categories