Remove the resize animation on view visibility GONE - java

I am trying to simulate the iPhone X like navigation on Android using ViewPager. When the user releases the bar on the bottom, the viewpager's visibility is set to View.GONE and it shows the ugly drop down like animation as shown in the video https://youtu.be/k2EXKFulfzU.
I tried to remove the animation by directly changing the dimension of viewpager then changing it's visibility to GONE and it doesn't help. Thank you in advanced :)
FrameLayout ll = mLayout.findViewById(R.id.mainLL);
ll.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, final MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
bar.animate().alpha(0.0f);
viewPager.setVisibility(View.VISIBLE);
layoutParams.height = old_y;
viewPager.setLayoutParams(layoutParams);
break;
case MotionEvent.ACTION_UP:
Animation a = new Animation() {
#Override
protected void applyTransformation(float factor, Transformation t) {
float factorX = (float)scaleX + factor;
int currentPos = viewPager.getCurrentItem();
if (currentPos != 0) {
adapter.getItem(currentPos - 1).setScaleX(Math.min(factorX, 1));
adapter.getItem(currentPos - 1).setScaleY(Math.min(factorX, 1));
}
adapter.getItem(currentPos).setScaleX(Math.min(factorX, 1));
adapter.getItem(currentPos).setScaleY(Math.min(factorX, 1));
adapter.getItem(currentPos + 1).setScaleX(Math.min(factorX, 1));
adapter.getItem(currentPos + 1).setScaleY(Math.min(factorX, 1));
if(adapter.getItem(currentPos).getScaleX() > 0.1) {
if(factor == 1){
viewPager.setVisibility(View.GONE);
}
}
}
};
bar.animate().alpha(1.0f);
viewPager.setVisibility(View.GONE);
a.setDuration(zoomInDuration);
viewPager.startAnimation(a);
break;
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/navigationLayout"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:animateLayoutChanges="false"
android:orientation="vertical">
<com.thanoscorp.uigestures.ViewPager.ViewPager
android:id="#+id/appViewpager"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top"
android:visibility="gone" />
<LinearLayout
android:id="#+id/barLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:orientation="vertical">
<ImageView
android:id="#+id/overlayBar"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_gravity="top"
android:paddingLeft="100dp"
android:paddingRight="100dp"
android:src="#mipmap/bar"
android:tint="#FF0000" />
</LinearLayout>
</FrameLayout>

I wasn't able to get rid of the animation, instead, i created another layout and added it to the window manager using wm.addView(vpLayout, lp); and removed it using wm.removeView(vpLayout);
and in case you are wondering what vpLayout is, I have seperated the ViewPager into another layout file and inflated it into a FrameLayout whose name is vpLayout. It took a while and it was worth it, but it gave birth to a new problem. Hope I will solve it too. Thanks :)

Related

Android - fixed header over scrollview with pull to zoom effect

i am currently rewriting our app from ios to android and I couln't get a proper effect to work. So in iOS we used the this library to achieve the fixed header over the content scrollview with parallax effect. I quickly modified the example and export it as a gif so it's more clear what we're trying to achieve:
So first of all I tried something with CollapsingToolbarLayout where I came across this snippet. So basically the snippet adds a AppBarLayout.Behavior what achieves the zoom effect. Overhaul it does not feel smooth.
Next I found PullZoomView which seems popular in android. The zoom and the scroll behaviour feels great. The only problem is that the header is not fixed and gets overscrolled by the listview like this:
So I dig into the code and tried to modified it for my propose but it seems hacky and complicated.
So it seems like from what I saw in all libraries that this overscroll over the header behaviour is more in common. Does someone has any advice how can I achieve the behaviour like I want before I write my own solution? Is there any library maybe which I don't found?
Doesn't directly answer your question but I made a library that has a component which expands on click/touch. I think you might be able to use the code to create your own scrollbar header thing.
https://github.com/NadavTasher/ToolLibs/blob/master/lightool/src/main/java/nadav/tasher/lightool/graphics/views/ExpandingView.java
e.g. Make the fixed view your header, and the expanding view your scrollview
I wrote a solution by my own right after I submit this question. I really liked the concept in the library from iOS that you can have individual UIViewController for the header and for the content part so in Android it seems that Fragments are the way to go here. I want to share my code if anyone want to have the same effect:
Add the following dependencies to yur build.gradle:
implementation 'me.everything:overscroll-decor-android:1.0.4'
implementation 'com.github.ksoichiro:android-observablescrollview:1.6.0'
My class (sorry it's a bit messy):
public abstract class ParallaxHeaderActivity extends AppCompatActivity implements ObservableScrollViewCallbacks {
private ObservableScrollView scrollView;
private LinearLayout headerView;
private FrameLayout contentView;
private int headerHeight = 0;
private int minimumHeaderHeight = 0;
protected void setContentView(int layout, Fragment header, Fragment content){
super.setContentView(layout);
ViewGroup rootView = (ViewGroup) ((ViewGroup) this
.findViewById(android.R.id.content)).getChildAt(0);
headerHeight = (int)convertDpToPixel(260, this);
minimumHeaderHeight = (int)convertDpToPixel(160, this);
int contentViewId = View.generateViewId();
contentView = new FrameLayout(this);
contentView.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
contentView.setPadding(0, headerHeight, 0, 0);
contentView.setId(contentViewId);
scrollView = new ObservableScrollView(this);
scrollView.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
scrollView.setScrollViewCallbacks(this);
scrollView.setFillViewport(true);
scrollView.addView(contentView);
scrollView.setScrollViewCallbacks(this);
IOverScrollDecor decor = new VerticalOverScrollBounceEffectDecorator(new ScrollViewOverScrollDecorAdapter(scrollView));
decor.setOverScrollUpdateListener(new IOverScrollUpdateListener() {
#Override
public void onOverScrollUpdate(IOverScrollDecor decor, int state, float offset) {
if (offset > 0) {
// 'view' is currently being over-scrolled from the top.
update((int)-offset);
}
}
});
rootView.addView(scrollView);
addFragment(contentViewId, content);
int headerViewId = View.generateViewId();
headerView = new LinearLayout(this);
headerView.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, headerHeight));
headerView.setId(headerViewId);
rootView.addView(headerView);
addFragment(headerViewId, header);
}
#Override
public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {
update(scrollY);
}
#Override
public void onDownMotionEvent() {
}
#Override
public void onUpOrCancelMotionEvent(ScrollState scrollState) {
}
private void addFragment(int id, Fragment fragment){
final FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(id, fragment);
transaction.addToBackStack(null);
transaction.commit();
}
private void update(final int scrollY) {
headerView.getLayoutParams().height = Math.max(headerHeight - scrollY, minimumHeaderHeight);
headerView.requestLayout();
}
public static float convertDpToPixel(float dp, Context context){
return dp * ((float) context.getResources().getDisplayMetrics().densityDpi / DisplayMetrics.DENSITY_DEFAULT);
}
}
In your Activity you can call it like:
setContentView(R.layout.activity_main, new HeaderFragment(), new ContentFragment());
fragment_content.xml:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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:layout_width="match_parent"
android:layout_height="wrap_content"
tools:context=".ContentFragment">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#android:color/white"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingBottom="#dimen/activity_vertical_margin"
android:text="#string/lipsum"/>
</FrameLayout>
fragment_header.xml:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:layout_width="0dp"
android:layout_height="0dp"
android:scaleType="centerCrop"
android:src="#drawable/example"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:text="Test"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:ellipsize="end"
android:gravity="center_vertical"
android:maxLines="1"
android:minHeight="?attr/actionBarSize"
android:textColor="#android:color/white"
android:textSize="20sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>

How to Overcome Aliasing Problem Caused By ConstraintLayout Guidelines?

I am trying to reconcile the following two things:
A) I want a precise, uniform, and clean UI with several identically sized buttons that correspond exactly to the underlying 'grid cells' -- A UI that will look as similar as possible (proportionally to screen size) across as many Android devices as possible.
B) On Android, the screen dimensions (aspect ratio and actual pixel numbers) of the user's device are unknown (to the app) until runtime.
My solution to this was to: (there is a code example below!)
1) Lock the app to portrait mode,
2) Do not define anything in static/absolute terms like dp,px, etc. and instead conceptualize a 'basic unit of measure' that is a function of screen height -- 0.08% in my case -- and base everything off of that.
3) Set horizontal guidelines within a ConstraintLayout whose positions are expressed as a percentage of parent (screen) height.
4) Make all buttons use this 'basic unit' as their height and width by setting their XML layout_constraintDimensionRatio attribute to "1:1" and using the guidelines above (see step 3),
5) Accomplish positioning and dimensions of all views by using constraints to either these guidelines, the parent's bounds, or one additional vertical guideline at 50% of screen width.
The problem is that depending on the pixel height of the screen (whether it happens to be odd or even... or maybe other factors), the dimensions of a view/button, (and thus the paths drawn inside it) constrained between one pair of guidelines does not exactly match those of another view drawn between some other pair... even though the distance between both pairs of guidelines should be the same percentage of parent height. :)
Here is an example showing the Nexus 4 emulator:
At first I thought the problem was simply due to rounding 'error' during Android's dimension calculations, but then why would the view not be square even though they are prescribed the 1:1 ratio attribute?
The only solutions I can think of would be:
A) To do the layout programatically instead of with XML... and set the guideline positions as exact pixel locations instead of percentages, and answer the question, "what is 0.08 x screen height?" myself... making the appropriate corrections to compensate for 'indivisible' screen heights.
B) Override onLayout() in the custom views and "force" their dimensions to be consistent... but then this would defeat the purpose of guidelines. :(
But I'm really hoping there is an easier solution than A or B.
(I know someone is going to suggest GridLayout, but it's not an option, for a few reasons... one of which is that in GridLayout, views inside cells must be set to wrap_content... which means the paths they draw cannot be generated relative to parent at runtime).
Thanks for any other suggestions, though.
Code Example:
I whipped up a simple 'minimal example' below that should be easy to reconstruct in Android Studio. The logs will reveal the issue if it's not immediately apparent.
XML layout:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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:id="#+id/rootView"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<android.support.constraint.Guideline
android:id="#+id/guidelineHorizontalTop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.08" />
<android.support.constraint.Guideline
android:id="#+id/guidelineHorizontalBottom"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.92" />
<android.support.constraint.Guideline
android:id="#+id/guidelineHorizontalCenter1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.38" />
<android.support.constraint.Guideline
android:id="#+id/guidelineHorizontalCenter2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.46" />
<android.support.constraint.Guideline
android:id="#+id/guidelineHorizontalCenter3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.54" />
<android.support.constraint.Guideline
android:id="#+id/guidelineHorizontalCenter4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.62" />
<com.example.boober.stack_aliasingproblem.CustomButton
android:id="#+id/buttonTopLeft"
android:layout_width="0dp"
android:layout_height="0dp"
android:tag="buttonTopLeft"
app:layout_constraintBottom_toTopOf="#+id/guidelineHorizontalTop"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.example.boober.stack_aliasingproblem.CustomButton
android:id="#+id/buttonTopRight"
android:layout_width="0dp"
android:layout_height="0dp"
android:tag="buttonTopRight"
app:layout_constraintBottom_toTopOf="#+id/guidelineHorizontalTop"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.example.boober.stack_aliasingproblem.CustomButton
android:id="#+id/buttonBottomLeft"
android:layout_width="0dp"
android:layout_height="0dp"
android:tag="buttonBottomLeft"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="#+id/guidelineHorizontalBottom" />
<com.example.boober.stack_aliasingproblem.CustomButton
android:id="#+id/buttonBottomRight"
android:layout_width="0dp"
android:layout_height="0dp"
android:tag="buttonBottomRight"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="#+id/guidelineHorizontalBottom" />
<com.example.boober.stack_aliasingproblem.CustomButton
android:id="#+id/buttonMiddle"
android:layout_width="0dp"
android:layout_height="0dp"
android:tag="buttonMiddle"
app:layout_constraintBottom_toBottomOf="#id/guidelineHorizontalCenter3"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="#id/guidelineHorizontalCenter2" />
<com.example.boober.stack_aliasingproblem.CustomButton
android:id="#+id/buttonMiddleTopLeft"
android:layout_width="0dp"
android:layout_height="0dp"
android:tag="buttonMiddleTopLeft"
app:layout_constraintBottom_toBottomOf="#id/guidelineHorizontalCenter2"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintEnd_toStartOf="#id/buttonMiddle"
app:layout_constraintTop_toTopOf="#id/guidelineHorizontalCenter1" />
<com.example.boober.stack_aliasingproblem.CustomButton
android:id="#+id/buttonMiddleTopRight"
android:layout_width="0dp"
android:layout_height="0dp"
android:tag="buttonMiddleTopRight"
app:layout_constraintBottom_toBottomOf="#id/guidelineHorizontalCenter2"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintStart_toEndOf="#id/buttonMiddle"
app:layout_constraintTop_toTopOf="#id/guidelineHorizontalCenter1" />
</android.support.constraint.ConstraintLayout>
MainActivity.java:
public class MainActivity extends AppCompatActivity {
CustomButton buttonTopLeft;
CustomButton buttonTopRight;
CustomButton buttonMiddle;
CustomButton buttonMiddleTopLeft;
CustomButton getButtonMiddleTopRight;
CustomButton buttonBottomLeft;
CustomButton buttonBottomRight;
CustomButton[] arrayOfCustomButtons;
ConstraintLayout rootView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
buttonTopLeft = findViewById(R.id.buttonTopLeft);
buttonTopRight = findViewById(R.id.buttonTopRight);
buttonBottomLeft = findViewById(R.id.buttonBottomLeft);
buttonBottomRight = findViewById(R.id.buttonBottomRight);
buttonMiddle = findViewById(R.id.buttonMiddle);
buttonMiddleTopLeft = findViewById(R.id.buttonMiddleTopLeft);
getButtonMiddleTopRight = findViewById(R.id.buttonMiddleTopRight);
arrayOfCustomButtons = new CustomButton[]{buttonTopLeft, buttonTopRight, buttonBottomLeft,
buttonBottomRight, buttonMiddle, buttonMiddleTopLeft, getButtonMiddleTopRight};
rootView = findViewById(R.id.rootView);
for (final CustomButton cb : arrayOfCustomButtons) {
cb.setClickable(true);
cb.post(new Runnable() {
#Override
public void run() {
Log.i("XXX", "width of: " + cb.getTag() + " is: "
+ cb.getMeasuredWidth());
}
});
}
rootView.post(new Runnable() {
#Override
public void run() {
Log.i("XXX", "height of rootView is: " + rootView.getMeasuredHeight());
}
});
}
}
CustomButton.java:
public class CustomButton extends View {
Path myOutlinePath;
Paint myThinPaintBrush;
Paint myThickPaintBrush;
boolean isHighlighted = false;
public CustomButton(Context context) {
super(context);
init();
}
public CustomButton(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public CustomButton(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
float measuredWidth = getMeasuredWidth();
Log.i("XXX", "measured WIDTH Of " + this.getTag() + " is: " + measuredWidth);
Log.i("XXX", "measured HEIGT Of " + this.getTag() + " is: " + getMeasuredHeight());
Log.i("XXX", "\n ");
generateMyOutline(measuredWidth);
myThinPaintBrush.setStrokeWidth(measuredWidth/12);
myThickPaintBrush.setStrokeWidth(measuredWidth/6);
}
private void generateMyOutline(float W) {
Path path = new Path();
path.moveTo(0,0);
path.lineTo(W, 0);
path.lineTo(W, W);
path.lineTo(0, W);
path.lineTo(0,0);
myOutlinePath = path;
}
private void init() {
myOutlinePath = new Path();
myThinPaintBrush = new Paint();
myThinPaintBrush.setAntiAlias(false); // setting this to true does not solve the problem.
myThinPaintBrush.setStyle(Paint.Style.STROKE);
myThinPaintBrush.setStrokeCap(Paint.Cap.ROUND);
myThickPaintBrush = new Paint();
myThickPaintBrush.setAntiAlias(false);
myThickPaintBrush.setStyle(Paint.Style.STROKE);
myThickPaintBrush.setStrokeCap(Paint.Cap.ROUND);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (this.isClickable()) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
isHighlighted = true;
invalidate();
break;
case MotionEvent.ACTION_UP:
isHighlighted = false;
invalidate();
break;
case MotionEvent.ACTION_CANCEL:
isHighlighted = false;
invalidate();
break;
}
}
return super.onTouchEvent(event);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawPath(myOutlinePath, myThinPaintBrush);
if (isHighlighted) {
canvas.drawPath(myOutlinePath, myThickPaintBrush);
}
super.onDraw(canvas);
}
}
I would go for the middle ground: Use your XML layout as is and make adjustments programmatically to the guideline positions. The following code converts percentage guidelines to fixed position guidelines by computing a new layout height that is a multiple of 8% of the height of the initial layout.
All sizes are computed correctly except for the bottom squares that tend to be larger. This can be easily corrected based upon your actual requirements (more important to be at the bottom or a certain distance from the other squares, for instance.)
MainActivity.jav
public class MainActivity extends AppCompatActivity {
CustomButton buttonTopLeft;
CustomButton buttonTopRight;
CustomButton buttonMiddle;
CustomButton buttonMiddleTopLeft;
CustomButton getButtonMiddleTopRight;
CustomButton buttonBottomLeft;
CustomButton buttonBottomRight;
CustomButton[] arrayOfCustomButtons;
ConstraintLayout rootView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
buttonTopLeft = findViewById(R.id.buttonTopLeft);
buttonTopRight = findViewById(R.id.buttonTopRight);
buttonBottomLeft = findViewById(R.id.buttonBottomLeft);
buttonBottomRight = findViewById(R.id.buttonBottomRight);
buttonMiddle = findViewById(R.id.buttonMiddle);
buttonMiddleTopLeft = findViewById(R.id.buttonMiddleTopLeft);
getButtonMiddleTopRight = findViewById(R.id.buttonMiddleTopRight);
rootView = findViewById(R.id.rootView);
rootView.post(new Runnable() {
#Override
public void run() {
int rootViewHeight = rootView.getMeasuredHeight();
Log.i("XXX", "height of rootView is: " + rootViewHeight);
int segHeight = (int) (rootViewHeight * 0.08f);
adjustGuideline(R.id.guidelineHorizontalTop, segHeight);
adjustGuideline(R.id.guidelineHorizontalCenter1, segHeight);
adjustGuideline(R.id.guidelineHorizontalCenter2, segHeight);
adjustGuideline(R.id.guidelineHorizontalCenter3, segHeight);
adjustGuideline(R.id.guidelineHorizontalCenter4, segHeight);
adjustGuideline(R.id.guidelineHorizontalBottom, segHeight);
arrayOfCustomButtons = new CustomButton[]{buttonTopLeft, buttonTopRight, buttonBottomLeft,
buttonBottomRight, buttonMiddle, buttonMiddleTopLeft, getButtonMiddleTopRight};
rootView = findViewById(R.id.rootView);
for (final CustomButton cb : arrayOfCustomButtons) {
cb.setClickable(true);
cb.post(new Runnable() {
#Override
public void run() {
Log.i("MainActivity", "<<<< width of: " + cb.getTag() + " is: "
+ cb.getMeasuredWidth());
}
});
}
}
});
}
private void adjustGuideline(int guideLineId, int segHeight) {
Guideline gl = (Guideline) findViewById(guideLineId);
ConstraintLayout.LayoutParams lp = ((ConstraintLayout.LayoutParams) gl.getLayoutParams());
gl.setGuidelineBegin((int) (segHeight * lp.guidePercent / 0.08f));
gl.setGuidelinePercent(-1f);
}
}

How to get view position in window while this view is in a layout which is in another layout?

I have tried this one (set to Buttons):
#Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
lastTouchXonWindow = event.getX() + v.getX();
lastTouchYonWindow = event.getY() + v.getY();
}
return false;
}
Which works while the view is in the only one layout to the activity but if it's in a layout which in another layout that doesn't works because v.getX() give me the X location in the parent layout and not in the window.
Here is an example of the activity layout:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center">
<!-- Some Widgets ...-->
<LinearLayout
android:id="#+id/ChallengeLinearChoix12"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center">
<Button
android:id="#+id/ChallengeButtonChoix1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="01"
style="#style/TresGrand"/>
<Button
android:id="#+id/ChallengeButtonChoix2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="02"
style="#style/TresGrand"/>
</LinearLayout>
</LinearLayout>
FurtherMore I have tried this which generate error "Cannot resolve the method getY()":
lastTouchY = event.getY() + v.getY() + v.getParent().getY();
There is a solution with .getRawX() for the absolute touch's location (thanks to pistolcaffe) then .getLocationOnScreen() for the absolute view's location
#Override
public boolean onTouch(View v, MotionEvent event) {
int[] location = new int[2];
layoutParent.getLocationOnScreen(location);
if (event.getAction() == MotionEvent.ACTION_DOWN) {
lastTouchX = event.getRawX() - location[0];
lastTouchY = event.getRawY() - location[1];
textAnimationScore.setText(""+location[1]);
}
return false;
}
I had to add the parent layout in the Java Activity, I'm surprised that v.getRootView().getLocationOnScreen(location) give 0 instead 160.

Android: Click outside of a listview in a listfragment

I have a listview which occupies all the space of the layout IN THEORY, but actually it occupies only half of the layout because it has got only 5 elements and it doesn't cover all the screen. I'd like to know when I touch OUTSIDE of the listview. I tried to create a clicklistener method for the layout of the listfragment which contains the list, but it is never used because IN THEORY the listview occupies all the layout, so the click isn't found. It is the same for the layout of the activity more or less. In that case the click is found only on the edges, so I can't find a method to solve my problem.
Here is the fragment layout:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#android:id/list" />
Here is the activity layout:
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
android:paddingBottom="#dimen/activity_vertical_margin"
tools:context="com.example.utente.actionbar.MainActivity">
<Button
android:text="MULTI"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="26dp"
android:id="#+id/button" />
<Button
android:text="SINGOLO"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="#+id/button"
android:layout_alignLeft="#+id/button"
android:layout_alignStart="#+id/button"
android:layout_marginBottom="25dp"
android:id="#+id/button2" />
IN THEORY the listview occupies all the layout
Not a theory, that is exactly what happens; by using android:layout_height="match_parent" the View will always take the full screen height.
listView.setOnClickListener would work if you want to see if you clicked anywhere in the ListView, but you typically would instead want listView.setOnItemClickListener to see if you have clicked on any single item, and not the entire list.
Refer: difference between onClickListener and onItemClickListener
If you really want to shrink the ListView, then android:layout_height="wrap_content" is an option, but I'm not sure that works without content actually being loaded into that View since the content wrapping is applied at inflation-time, which since it has no adapter set, can't be done.
If you are needing to detect a listener literally "outside the ListView", then you need to set some type of click / touch listener on the rootView of that Fragment.
public View onCreateView(...) {
rootView = inflater.inflate(...);
listView = ...;
listView.setOnTouchListner(...
#Override
public boolean onTouch(View v, MotionEvent event) {
return false; // Says that click was not handled here.
}
});
rootView.setOnTouchListner(...
// TODO: Check if click landed outside the ListView
#Override
public boolean onTouch(View v, MotionEvent event) {
if (v.getId() != android.R.id.listView) { // Not the list
if (event.getAction() == MotionEvent.ACTION_UP) {
// Up action more reliable than "down"
return true;
}
}
}
);
return rootView;
}
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:layout_width="match_parent"
android:layout_height="wrap_content" <!-- Or specific height -->
android:id="#android:id/list" />
<View
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/clickView" />
</LinearLayout>
In Fragmet
public View onCreateView(...) {
rootView = inflater.inflate(...);
View clickView = rootView.findViewById(...);
clickView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// Do something here
}
});
return rootView;
}
I guess this solves your problem.

Android adding simple animations while setvisibility(view.Gone)

I have designed a simple layout.I have finished the design without animation, but now I want to add animations when textview click event and I don't know how to use it.
Did my xml design looks good or not?
Any suggestions would be appreciated.
My XML
<?xml version="1.0" encoding="UTF-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:longClickable="false"
android:orientation="vertical"
android:weightSum="16" >
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="0dp"
android:orientation="vertical"
android:background="#00DDA0"
android:layout_weight="3" >
</LinearLayout>
<TextView
android:id="#+id/Information1"
android:layout_width="match_parent"
android:layout_height="1dp"
android:text="Child Information"
android:background="#0390BE"
android:layout_weight="0.75"
android:textColor="#FFFFFF"
android:layout_gravity="center|fill_horizontal"/>
<LinearLayout
android:id="#+id/layout1"
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="8.5"
android:background="#BBBBBB"
android:orientation="vertical" >
<TextView
android:id="#+id/textView1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="TextView" />
</LinearLayout>
<TextView
android:id="#+id/Information2"
android:layout_width="match_parent"
android:layout_height="0dp"
android:text="Parent Information"
android:background="#0390BE"
android:layout_weight="0.75"
android:textColor="#FFFFFF"
android:layout_gravity="center|fill_horizontal"/>
<LinearLayout
android:id="#+id/layout2"
android:layout_width="fill_parent"
android:layout_height="0dp"
android:orientation="vertical"
android:background="#BBBBBB"
android:layout_weight="8.5" >
<TextView
android:id="#+id/textView2"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="TextView" />
</LinearLayout>
<TextView
android:id="#+id/Information3"
android:layout_width="match_parent"
android:layout_height="0dp"
android:text="Siblings"
android:background="#0390BE"
android:layout_weight="0.75"
android:textColor="#FFFFFF"
android:layout_gravity="center|fill_horizontal"/>
<LinearLayout
android:id="#+id/layout3"
android:layout_width="fill_parent"
android:layout_height="0dp"
android:orientation="vertical"
android:background="#BBBBBB"
android:layout_weight="8.5" >
<TextView
android:id="#+id/textView3"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="TextView" />
</LinearLayout>
<TextView
android:id="#+id/Information4"
android:layout_width="match_parent"
android:layout_height="0dp"
android:text="Teacher Information"
android:background="#0390BE"
android:layout_weight="0.75"
android:textColor="#FFFFFF"
android:layout_gravity="center|fill_horizontal"/>
<LinearLayout
android:id="#+id/layout4"
android:layout_width="fill_parent"
android:layout_height="0dp"
android:orientation="vertical"
android:background="#BBBBBB"
android:layout_weight="8.5" >
<TextView
android:id="#+id/textView4"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="TextView" />
</LinearLayout>
<TextView
android:id="#+id/Information5"
android:layout_width="match_parent"
android:layout_height="0dp"
android:text="Grade Information"
android:background="#0390BE"
android:layout_weight="0.75"
android:textColor="#FFFFFF"
android:layout_gravity="center|fill_horizontal"/>
<LinearLayout
android:id="#+id/layout5"
android:layout_width="fill_parent"
android:layout_height="0dp"
android:orientation="vertical"
android:background="#BBBBBB"
android:layout_weight="8.5" >
<TextView
android:id="#+id/textView5"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="TextView" />
</LinearLayout>
<TextView
android:id="#+id/Information6"
android:layout_width="match_parent"
android:layout_height="0dp"
android:text="Health Information"
android:background="#0390BE"
android:layout_weight="0.75"
android:textColor="#FFFFFF"
android:layout_gravity="center|fill_horizontal"/>
<LinearLayout
android:id="#+id/layout6"
android:layout_width="fill_parent"
android:layout_height="0dp"
android:orientation="vertical"
android:background="#BBBBBB"
android:layout_weight="8.5" >
<TextView
android:id="#+id/textView5"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="TextView"
android:layout_weight="8.5" />
</LinearLayout>
</LinearLayout>
My java
public class Certify_Info extends Activity {
private static TextView tv2,tv3,tv5,tv6,tv4,tv1;
private static LinearLayout l1,l2,l3,l4,l5,l6;
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_certify__info);
tv1=(TextView) findViewById(R.id.Information1);
tv2=(TextView) findViewById(R.id.Information2);
tv3=(TextView) findViewById(R.id.Information3);
tv4=(TextView) findViewById(R.id.Information4);
tv5=(TextView) findViewById(R.id.Information5);
tv6=(TextView) findViewById(R.id.Information6);
l1=(LinearLayout) findViewById(R.id.layout1);
l2=(LinearLayout) findViewById(R.id.layout2);
l3=(LinearLayout) findViewById(R.id.layout3);
l4=(LinearLayout) findViewById(R.id.layout4);
l5=(LinearLayout) findViewById(R.id.layout5);
l6=(LinearLayout) findViewById(R.id.layout6);
l2.setVisibility(View.GONE);
l3.setVisibility(View.GONE);
l4.setVisibility(View.GONE);
l5.setVisibility(View.GONE);
l6.setVisibility(View.GONE);
tv1.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
l2.setVisibility(View.GONE);
l3.setVisibility(View.GONE);
l4.setVisibility(View.GONE);
l5.setVisibility(View.GONE);
l6.setVisibility(View.GONE);
l1.setVisibility(View.VISIBLE);
}
});
tv2.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
l1.setVisibility(View.GONE);
l3.setVisibility(View.GONE);
l4.setVisibility(View.GONE);
l5.setVisibility(View.GONE);
l6.setVisibility(View.GONE);
l2.setVisibility(View.VISIBLE);
}
});
tv3.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
l1.setVisibility(View.GONE);
l2.setVisibility(View.GONE);
l4.setVisibility(View.GONE);
l5.setVisibility(View.GONE);
l6.setVisibility(View.GONE);
l3.setVisibility(View.VISIBLE);
}
});
tv4.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
l1.setVisibility(View.GONE);
l2.setVisibility(View.GONE);
l3.setVisibility(View.GONE);
l4.setVisibility(View.GONE);
l5.setVisibility(View.GONE);
l6.setVisibility(View.GONE);
l4.setVisibility(View.VISIBLE);
}
});
tv5.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
l1.setVisibility(View.GONE);
l2.setVisibility(View.GONE);
l3.setVisibility(View.GONE);
l4.setVisibility(View.GONE);
l6.setVisibility(View.GONE);
l5.setVisibility(View.VISIBLE);
}
});
tv6.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
l1.setVisibility(View.GONE);
l2.setVisibility(View.GONE);
l3.setVisibility(View.GONE);
l4.setVisibility(View.GONE);
l5.setVisibility(View.GONE);
l6.setVisibility(View.VISIBLE);
}
});
}
}
You can do two things to add animations, first you can let android animate layout changes for you. That way every time you change something in the layout like changing view visibility or view positions android will automatically create fade/transition animations. To use that set
android:animateLayoutChanges="true"
on the root node in your layout.
Your second option would be to manually add animations. For this I suggest you use the new animation API introduced in Android 3.0 (Honeycomb). I can give you a few examples:
This fades out a View:
view.animate().alpha(0.0f);
This fades it back in:
view.animate().alpha(1.0f);
This moves a View down by its height:
view.animate().translationY(view.getHeight());
This returns the View to its starting position after it has been moved somewhere else:
view.animate().translationY(0);
You can also use setDuration() to set the duration of the animation. For example this fades out a View over a period of 2 seconds:
view.animate().alpha(0.0f).setDuration(2000);
And you can combine as many animations as you like, for example this fades out a View and moves it down at the same time over a period of 0.3 seconds:
view.animate()
.translationY(view.getHeight())
.alpha(0.0f)
.setDuration(300);
And you can also assign a listener to the animation and react to all kinds of events. Like when the animation starts, when it ends or repeats etc. By using the abstract class AnimatorListenerAdapter you don't have to implement all callbacks of AnimatorListener at once but only those you need. This makes the code more readable. For example the following code fades out a View moves it down by its height over a period of 0.3 seconds (300 milliseconds) and when the animation is done its visibility is set to View.GONE.
view.animate()
.translationY(view.getHeight())
.alpha(0.0f)
.setDuration(300)
.setListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
view.setVisibility(View.GONE);
}
});
The easiest way to animate Visibility changes is use Transition API which available in support (androidx) package. Just call TransitionManager.beginDelayedTransition method then change visibility of the view. There are several default transitions like Fade, Slide.
import androidx.transition.TransitionManager;
import androidx.transition.Transition;
import androidx.transition.Fade;
private void toggle() {
Transition transition = new Fade();
transition.setDuration(600);
transition.addTarget(R.id.image);
TransitionManager.beginDelayedTransition(parent, transition);
image.setVisibility(show ? View.VISIBLE : View.GONE);
}
Where parent is parent ViewGroup of animated view. Result:
Here is result with Slide transition:
import androidx.transition.Slide;
Transition transition = new Slide(Gravity.BOTTOM);
It is easy to write custom transition if you need something different. Here is example with CircularRevealTransition which I wrote in another answer. It shows and hide view with CircularReveal animation.
Transition transition = new CircularRevealTransition();
android:animateLayoutChanges="true" option does same thing, it just uses AutoTransition as transition.
Try adding this line to the xml parent layout
android:animateLayoutChanges="true"
Your layout will look like this
<?xml version="1.0" encoding="UTF-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:animateLayoutChanges="true"
android:longClickable="false"
android:orientation="vertical"
android:weightSum="16">
.......other code here
</LinearLayout>
Please check this link. Which will allow animations like L2R, R2L, T2B, B2T animations.
This code shows animation from left to right
TranslateAnimation animate = new TranslateAnimation(0,view.getWidth(),0,0);
animate.setDuration(500);
animate.setFillAfter(true);
view.startAnimation(animate);
view.setVisibility(View.GONE);
if you want to do it from R2L then use
TranslateAnimation animate = new TranslateAnimation(0,-view.getWidth(),0,0);
for top to bottom as
TranslateAnimation animate = new TranslateAnimation(0,0,0,view.getHeight());
and vice a versa..
Base on #ashakirov answer, here is my extension to show/hide view with fade animation
fun View.fadeVisibility(visibility: Int, duration: Long = 400) {
val transition: Transition = Fade()
transition.duration = duration
transition.addTarget(this)
TransitionManager.beginDelayedTransition(this.parent as ViewGroup, transition)
this.visibility = visibility
}
Example using
view.fadeVisibility(View.VISIBLE)
view.fadeVisibility(View.GONE, 2000)
I was able to show/hide a menu this way:
MenuView.java (extends FrameLayout)
private final int ANIMATION_DURATION = 500;
public void showMenu()
{
setVisibility(View.VISIBLE);
animate()
.alpha(1f)
.setDuration(ANIMATION_DURATION)
.setListener(null);
}
private void hideMenu()
{
animate()
.alpha(0f)
.setDuration(ANIMATION_DURATION)
.setListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
setVisibility(View.GONE);
}
});
}
Source
Based on the answer of #Xaver Kapeller I figured out a way to create scroll animation when new views appear on the screen (and also animation to hide them).
It goes from this state:
Button
Last Button
to
Button
Button 1
Button 2
Button 3
Button 4
Last Button
and viceversa.
So, when the user clicks on the first button, the elements "Button 1", "Button 2", "Button 3" and "Button 4" will appear using fade animation and the element "Last Button" will move down till end. The height of the layout will change as well, allowing using scroll view properly.
This is the code to show elements with animation:
private void showElements() {
// Precondition
if (areElementsVisible()) {
Log.w(TAG, "The view is already visible. Nothing to do here");
return;
}
// Animate the hidden linear layout as visible and set
// the alpha as 0.0. Otherwise the animation won't be shown
mHiddenLinearLayout.setVisibility(View.VISIBLE);
mHiddenLinearLayout.setAlpha(0.0f);
mHiddenLinearLayout
.animate()
.setDuration(ANIMATION_TRANSITION_TIME)
.alpha(1.0f)
.setListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
updateShowElementsButton();
mHiddenLinearLayout.animate().setListener(null);
}
})
;
mLastButton
.animate()
.setDuration(ANIMATION_TRANSITION_TIME)
.translationY(mHiddenLinearLayoutHeight);
// Update the high of all the elements relativeLayout
LayoutParams layoutParams = mAllElementsRelativeLayout.getLayoutParams();
// TODO: Add vertical margins
layoutParams.height = mLastButton.getHeight() + mHiddenLinearLayoutHeight;
}
and this is the code to hide elements of the animation:
private void hideElements() {
// Precondition
if (!areElementsVisible()) {
Log.w(TAG, "The view is already non-visible. Nothing to do here");
return;
}
// Animate the hidden linear layout as visible and set
mHiddenLinearLayout
.animate()
.setDuration(ANIMATION_TRANSITION_TIME)
.alpha(0.0f)
.setListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
Log.v(TAG, "Animation ended. Set the view as gone");
super.onAnimationEnd(animation);
mHiddenLinearLayout.setVisibility(View.GONE);
// Hack: Remove the listener. So it won't be executed when
// any other animation on this view is executed
mHiddenLinearLayout.animate().setListener(null);
updateShowElementsButton();
}
})
;
mLastButton
.animate()
.setDuration(ANIMATION_TRANSITION_TIME)
.translationY(0);
// Update the high of all the elements relativeLayout
LayoutParams layoutParams = mAllElementsRelativeLayout.getLayoutParams();
// TODO: Add vertical margins
layoutParams.height = mLastButton.getHeight();
}
Note there is a simple hack on the method to hide the animation. On the animation listener mHiddenLinearLayout, I had to remove the listener itself by using:
mHiddenLinearLayout.animate().setListener(null);
This is because once an animation listener is attached to an view, the next time when any animation is executed in this view, the listener will be executed as well. This might be a bug in the animation listener.
The source code of the project is on GitHub:
https://github.com/jiahaoliuliu/ViewsAnimated
Happy coding!
Update: For any listener attached to the views, it should be removed after the animation ends. This is done by using
view.animate().setListener(null);
My solution extension
fun View.slideVisibility(visibility: Boolean, durationTime: Long = 300) {
val transition = Slide(Gravity.BOTTOM)
transition.apply {
duration = durationTime
addTarget(this#slideVisibility)
}
TransitionManager.beginDelayedTransition(this.parent as ViewGroup, transition)
this.isVisible = visibility
}
Use:
textView.slideVisibility(true)
Find the below code to make visible the view in Circuler reveal, if you send true, it'll get Invisible/Gone. If you send false, it'll get visible. anyView is the view you're going to visible/hide, it could be any view (Layouts, Buttons etc)
private fun toggle(flag: Boolean, anyView: View) {
if (flag) {
val cx = anyView.width / 2
val cy = anyView.height / 2
val initialRadius = Math.hypot(cx.toDouble(), cy.toDouble()).toFloat()
val anim = ViewAnimationUtils.createCircularReveal(anyView, cx, cy, initialRadius, 0f)
anim.addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
super.onAnimationEnd(animation)
anyView.visibility = View.INVISIBLE
}
})
anim.start()
} else {
val cx = anyView.width / 2
val cy = anyView.height / 2
val finalRadius = Math.hypot(cx.toDouble(), cy.toDouble()).toFloat()
val anim = ViewAnimationUtils.createCircularReveal(anyView, cx, cy, 0f, finalRadius)
anyView.visibility = View.VISIBLE
anim.start()
}
}

Categories

Resources