If you use the last version of WhatsApp you will notice that if you long click a textbox in a chat, then the menu icons on the toolbar will change with a nice rotating animation.
How could I reproduce that effect? I know I should invalidate the menu but not how to make the animation.
Use a Toolbar.
Wait for the Toolbar to have its items inflated.
Find the item in question
Animate the item
Example:
mToolbar = (Toolbar) findViewById(R.id.toolbar);
mToolbar.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
#Override
public void onLayoutChange(View v, int left, int top, int right, int bottom,
int oldLeft, int oldTop, int oldRight, int oldBottom) {
View item = mToolbar.findViewById(R.id.action_add_item);
if (item != null) {
mToolbar.removeOnLayoutChangeListener(this);
item.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
ObjectAnimator animator = ObjectAnimator
.ofFloat(v, "rotation", v.getRotation() + 180);
animator.start();
}
});
}
}
});
Note R.id.action_add_item is the id attribute of the MenuItem.
Related
I'm working on a chat application. Now, while fetching messages from the back-end for the first time, i want my Linear Layout pointer to move to the last message in the list, so that the user always sees the latest messages on opening the chat fragment.
This is my code:
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(homeScreenActivity);
linearLayoutManager.setStackFromEnd(true);
linearLayoutManager.setReverseLayout(true);
chatRecycler.setLayoutManager(linearLayoutManager);
Thanks in advance!
If you dont know the when data is entered, you can make addOnLayoutChangeListener on your recycler view.
And in addOnLayoutChangeListener
chatRecycler.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
#Override
public void onLayoutChange(View v,
int left, int top, int right, int bottom,
int oldLeft, int oldTop, int oldRight, int oldBottom) {
if (bottom < oldBottom) {
chatRecycler.postDelayed(new Runnable() {
#Override
public void run() {
recycler_discussion.scrollToPosition(
recycler_discussion.getAdapter().getItemCount() - 1);
}
}, 50);
}
}
});
I'm trying to implement a ScrollView in Android that doesn't scroll when adding an item above the current scroll position.
The default implementation of ScrollView behaves as following:
Adding an item above the current scroll position:
Adding an item below the current scroll position:
How can I "lock" the ScrollView prior to adding an item above the current scroll position?
This is my layout file, I've currently overridden both the ScrollView and LinearLayout, but haven't made any alterations yet.
<LinearLayout
android:layout_height="fill_parent"
android:orientation="vertical"
android:layout_width="fill_parent">
<LinearLayout
android:id="#+id/LinearLayout02"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:layout_alignParentBottom="true">
<Button
android:id="#+id/Button02"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1" android:text="Add To Top"
android:onClick="addToStart">
</Button>
<Button
android:id="#+id/Button03"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Add to End"
android:onClick="addToEnd">
</Button>
</LinearLayout>
<com.poc.scroller.locable.lockablescrollerpoc.LockedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:fillViewport="true"
android:scrollbarAlwaysDrawVerticalTrack="true"
android:verticalScrollbarPosition="right"
android:fadeScrollbars="false"
android:background="#color/scrollColor">
<com.poc.scroller.locable.lockablescrollerpoc.LockedLinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:id="#+id/Container">
</com.poc.scroller.locable.lockablescrollerpoc.LockedLinearLayout>
</com.poc.scroller.locable.lockablescrollerpoc.LockedScrollView>
</LinearLayout>
</android.support.constraint.ConstraintLayout>
Example Source Code:
https://github.com/Amaros90/android-lockable-scroller-poc
Thank you!
You can easily get the opposite behavior by using addOnLayoutChangeListener of your LinearLayout and reset the ScrollView's ScrollY. Here is the implementation in your ScrollViewActivity.onCreate
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.scrollview);
_linearLayout = (LockedLinearLayout)findViewById(R.id.Container);
_scrollView = (LockedScrollView)findViewById(R.id.ScrollView);
_layoutInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
_linearLayout.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
#Override
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
_scrollView.scrollTo(0, _scrollView.getScrollY() + (bottom - oldBottom ));
}
});
addExampleImage(10, _linearLayout);
}
Then you can easily flag the addToEnd function or check where the child was added to avoid changing scroll when some child is added to the bottom.
You can try to use RecyclerView and implement onDataSetChanged(). Then detect whether add to TOP or add to END button was pressed. Then use scrollToPositionWithOffset(int, int) to manage the scroll.
For example:
//Scroll item 3 to 20 pixels from the top
linearLayoutManager.scrollToPositionWithOffset(3, 20);
For restoring the scroll position of a RecyclerView, this is how to save the scroll positions (the two arguments for the method scrollToPositionWithOffset(int, int)):
int index = linearLayoutManager.findFirstVisibleItemPosition();
View v = linearLayoutManager.getChildAt(0);
int top = (v == null) ? 0 : (v.getTop() - linearLayoutManager.getPaddingTop())
Implementing this worked for me. You can check out the example app I added in the original question.
public class LockableScrollView extends ScrollView {
private boolean _enabled = true;
public LockableScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
super.setFillViewport(true);
}
#Override
public void addView(View layout, ViewGroup.LayoutParams params) {
super.addView(layout, params);
((ViewGroup)layout).setOnHierarchyChangeListener(new OnHierarchyChangeListener() {
#Override
public void onChildViewAdded(View layout, View item) {
if (_enabled) {
item.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
#Override
public void onLayoutChange(View item, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
item.removeOnLayoutChangeListener(this);
int scrollViewY = ((LockableScrollView)item.getParent().getParent()).getScrollY();
int layoutPosition = ((View)item.getParent()).getTop();
boolean shouldScroll = item.getTop() + layoutPosition <= scrollViewY || item.getBottom() + getTop() <= scrollViewY;
if (shouldScroll) {
final int childViewHeight = item.getHeight();
((View)item.getParent()).addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
#Override
public void onLayoutChange(View layout, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
layout.removeOnLayoutChangeListener(this);
LockableScrollView scrollView = ((LockableScrollView)layout.getParent());
scrollView.scrollTo(scrollView.getScrollX(), scrollView.getScrollY() + childViewHeight);
}
});
}
}
});
}
}
#Override
public void onChildViewRemoved(View layout, View item) {
if (_enabled) {
int scrollViewY = ((LockableScrollView)layout.getParent()).getScrollY();
int layoutPosition = layout.getTop();
boolean shouldScroll = item.getTop() + layoutPosition <= scrollViewY || item.getBottom() + getTop() <= scrollViewY;
if (shouldScroll) {
final int childViewHeight = item.getHeight();
layout.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
#Override
public void onLayoutChange(View layout, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
layout.removeOnLayoutChangeListener(this);
LockableScrollView scrollView = ((LockableScrollView)layout.getParent());
scrollView.scrollTo(scrollView.getScrollX(), scrollView.getScrollY() - childViewHeight);
}
});
}
}
}
});
}
}
you can do it easily just get it..
first of all in onclick event store the int value by
using getFirstVisiblePosition()
example.
in onclick of add element button do this---
int currentpos = recycleview.getFirstVisiblePosition();
now after you insert elment to list/recyclerview---
recyclerview.scrolltoposition(currentpos);
thats it try if you want to do it without error..
Good Luck :)
I want to animate View right after it was added to parent (something like DrawerLayout). The problem is that View has varying size, and animation target position depends on that size. Simplified sample code:
AnimatingView extends View {
public int offsetX;
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int screenWidth = MeasureSpec.getSize(widthMeasureSpec);
final int screenHeight = MeasureSpec.getSize(heightMeasureSpec);
offsetX = calculateOffset(screenWidth);
...
}
}
Code similar to this triggers the animation:
#Override
public void onClick(View v) {
AnimatingView animatingView = new AnimatingView(getContext());
parentLayout.addView(animatingView);
animatingView.animate().x(animatingView.offsetX).setDuration(500).start();
}
In this case onMeasure() happens after animate(), so animation fails. What is the correct way of doing stuff which depends on view measuring?
The simple & stupid way would be something like animateOnceAfterMeasuring() based on isInitialized flag, but I don't think it the correct way of doing this.
this should work:
AnimatingView animatingView = new AnimatingView(getContext());
parentLayout.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
#Override
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
v.removeOnLayoutChangeListener(this);
animatingView.animate().x(animatingView.offsetX).setDuration(500).start();
}
});
You can use the ViewTreeObserver for this
#Override
public void onClick(View v) {
final AnimatingView animatingView = new AnimatingView(getContext());
parentLayout.addView(animatingView);
animatingView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
if (Build.VERSION.SDK_INT < 16) {
animatingView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
} else {
animatingView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
animatingView.animate().x(animatingView.offsetX).setDuration(500).start();
}
});
}
Standard android BottomSheetBehavior has tree state: hidden, collapsed and expanded.
I want to allow user to "leave" bottom sheet between collapsed and expanded. Now, with the default behavior, it will snap to collapsed or expanded based which is closest. How should I disable this snap functionality?
I will present a way to achievie such functionality for a View extending BottomSheetDialogFragment.
Expanding:
First of all overrive onResume:
#Override
public void onResume() {
super.onResume();
addGlobaLayoutListener(getView());
}
private void addGlobaLayoutListener(final View view) {
view.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
#Override
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
setPeekHeight(v.getMeasuredHeight());
v.removeOnLayoutChangeListener(this);
}
});
}
public void setPeekHeight(int peekHeight) {
BottomSheetBehavior behavior = getBottomSheetBehaviour();
if (behavior == null) {
return;
}
behavior.setPeekHeight(peekHeight);
}
What the code above is supposed to do is just setting the BottomSheet peekHeight to the heigth of the view. The key here is the function getBottomSheetBehaviour(). The implementation is below:
private BottomSheetBehavior getBottomSheetBehaviour() {
CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams) ((View) getView().getParent()).getLayoutParams();
CoordinatorLayout.Behavior behavior = layoutParams.getBehavior();
if (behavior != null && behavior instanceof BottomSheetBehavior) {
((BottomSheetBehavior) behavior).setBottomSheetCallback(mBottomSheetBehaviorCallback);
return (BottomSheetBehavior) behavior;
}
return null;
}
This just check if the parent of View has 'CoordinatorLayout.LayoutParams' set. If yes, sets appropriate BottomSheetBehavior.BottomSheetCallback (which is needed in the next part), and more importantly returns the CoordinatorLayout.Behavior, which is supposed to be BottomSheetBehavior.
Collapsing:
Here a [`BottomSheetBehavior.BottomSheetCallback.onSlide (View bottomSheet, float slideOffset)``](https://developer.android.com/reference/android/support/design/widget/BottomSheetBehavior.BottomSheetCallback.html#onSlide(android.view.View, float)) is just exactly what is needed. From the [documentation](https://developer.android.com/reference/android/support/design/widget/BottomSheetBehavior.BottomSheetCallback.html#onSlide(android.view.View, float)):
Offset increases as this bottom sheet is moving upward. From 0 to 1 the sheet is between collapsed and expanded states and from -1 to 0 it is between hidden and collapsed states.
This means, that just checking the second parameter is needed for collapse detection:
define BottomSheetBehavior.BottomSheetCallback in the same class:
private BottomSheetBehavior.BottomSheetCallback mBottomSheetBehaviorCallback = new BottomSheetBehavior.BottomSheetCallback() {
#Override
public void onStateChanged(#NonNull View bottomSheet, int newState) {
if (newState == BottomSheetBehavior.STATE_HIDDEN) {
dismiss();
}
}
#Override
public void onSlide(#NonNull View bottomSheet, float slideOffset) {
if (slideOffset < 0) {
dismiss();
}
}
};
How to do scrolling effect like twitter when scroll Up hide viewpager tab (Home, Discover, activity). Or effect like facebook scrolling, while scroll up hide option view(status, photo, checkin) when scroll down show option view. Any example link will do please help.
Easy solution:
public abstract class OnScrollObserver implements AbsListView.OnScrollListener {
public abstract void onScrollUp();
public abstract void onScrollDown();
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
int last = 0;
boolean control = true;
#Override
public void onScroll(AbsListView view, int current, int visibles, int total) {
if (current < last && !control) {
onScrollUp();
control = true;
} else if (current > last && control) {
onScrollDown();
control = false;
}
last = current;
}
Usage:
listView.setOnScrollListener(new OnScrollObserver() {
#Override
public void onScrollUp() {
}
#Override
public void onScrollDown() {
}
});
EDIT: better, you have this library https://github.com/ksoichiro/Android-ObservableScrollView
You can look at this https://github.com/LarsWerkman/QuickReturnListView
Source: https://stackoverflow.com/a/25304575/244702
That's my own implementation:
notice:
View to be hidden should be fixed height
We are not hiding the view by Visiblity.GONE
We are setting the final height to 0px
Here is the code:
//Your view which you would like to animate
final RelativeLayout yourViewToHide = (yourViewToHideativeLayout) findViewById(R.id.topWrapper);
//The initial height of that view
final int initialViewHeight = yourViewToHide.getLayoutParams().height;
listView.setOnScrollListener(new AbsListView.OnScrollListener() {
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
//Try catch block for NullPointerExceptions
try{
//Here is a simple delay. If user scrolls ListView from the top of the screen to the bottom then continue
if(firstVisibleItem % visibleItemCount == 0) {
//Here we initialize the animator, doesn't matter what values You will type in
ValueAnimator animator = ValueAnimator.ofInt(0, 1);
//if Scrolling up
if (fastScrollSB.getProgress() > view.getFirstVisiblePosition()){
//Getting actual yourViewToHide params
ViewGroup.LayoutParams params = yourViewToHide.getLayoutParams();
if (!animator.isRunning()) {
//Setting animation from actual value to the initial yourViewToHide height)
animator.setIntValues(params.height, initialViewHeight);
//Animation duration
animator.setDuration(500);
//In this listener we update the view
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
ViewGroup.LayoutParams params = yourViewToHide.getLayoutParams();
params.height = (int) animation.getAnimatedValue();
yourViewToHide.setLayoutParams(params);
}
});
//Starting the animation
animator.start();
}
System.out.println("Scrolling up!");
//If not scrolling
} else if (fastScrollSB.getProgress() == view.getFirstVisiblePosition()) {
System.out.println("Not Scrolling!");
//If scrolling down
} else if (fastScrollSB.getProgress() < view.getFirstVisiblePosition()){
//Getting actual yourViewToHide params
ViewGroup.LayoutParams params = yourViewToHide.getLayoutParams();
if (!animator.isRunning()) {
//Setting animation from actual value to the target value (here 0, because we're hiding the view)
animator.setIntValues(params.height, 0);
//Animation duration
animator.setDuration(500);
//In this listener we update the view
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
ViewGroup.LayoutParams params = yourViewToHide.getLayoutParams();
params.height = (int) animation.getAnimatedValue();
yourViewToHide.setLayoutParams(params);
}
});
//Starting the animation
animator.start();
}
System.out.println("Scrolling down!");
}
}
}catch (Exception e){
e.printStackTrace();
}
}
});`
Hope it fits your needs :)
there is no quick example for this. But what you can do is keep track which way you are scrolling and show or hide the view accordingly
For example get first visible position of the ListView keep track of this and if it is smaller then before you know you are scrolling up this way you can show the view. In case it is bigger then hide the view.
This is a simple approach in case you want to have more precise you need to work with onTouchListeners and y coordinates of the movement.
http://developer.android.com/reference/android/widget/ListView.html