By default scrollview's fading edge is visible only if it is possible to scroll in that direction. How can I make it visible at all times?
I don't want to put any drawables on top or something like that. I want to accomplish it using the builtin fading edge, probably by overriding some scrollview functions.
Yes, extend ScrollView and override these methods (based on Donut-release2):
#Override
protected float getTopFadingEdgeStrength() {
if (getChildCount() == 0) {
return 0.0f;
}
return 1.0f;
}
#Override
protected float getBottomFadingEdgeStrength() {
if (getChildCount() == 0) {
return 0.0f;
}
return 1.0f;
}
For comparison's sake, this is the original code, which shortens the fading edge as you get close to the end of the list:
#Override
protected float getTopFadingEdgeStrength() {
if (getChildCount() == 0) {
return 0.0f;
}
final int length = getVerticalFadingEdgeLength();
if (mScrollY < length) {
return mScrollY / (float) length;
}
return 1.0f;
}
#Override
protected float getBottomFadingEdgeStrength() {
if (getChildCount() == 0) {
return 0.0f;
}
final int length = getVerticalFadingEdgeLength();
final int bottomEdge = getHeight() - mPaddingBottom;
final int span = getChildAt(0).getBottom() - mScrollY - bottomEdge;
if (span < length) {
return span / (float) length;
}
return 1.0f;
}
You can also use the following code:
public class TopFadeEdgeScrollView extends ScrollView {
public TopFadeEdgeScrollView(Context context) {
super(context);
}
public TopFadeEdgeScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public TopFadeEdgeScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
protected float getBottomFadingEdgeStrength() {
return 0.0f;
}
}
Related
enter code here i am using recyclerview but i need to implement one more
recyclerview inside the adapter layout of the recyclerview. Can
anyone tell me how can i set the json data to the recyclerview
which is inside the cardview of verticsal recycler. Dont tell me
about how to set the horizontal recyclerview i know it very well.
the problem with me is that i am confused that how to set data to
recyclerview which is inside cardview of vertical recyclerview
You can pass a List> to the vertical RecyclerView's adapter and then in the onBindViewHolder, pass the inner list at some index i to the constructor of horizontal RecyclerView's adapter.
For the outer vertical RecyclerView, use this extended class:
public class BetterRecyclerView extends RecyclerView{
private static final int INVALID_POINTER = -1;
private int mScrollPointerId = INVALID_POINTER;
private int mInitialTouchX, mInitialTouchY;
private int mTouchSlop;
public BetterRecyclerView(Context context) {
this(context, null);
}
public BetterRecyclerView(Context context, #Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public BetterRecyclerView(Context context, #Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
final ViewConfiguration vc = ViewConfiguration.get(getContext());
mTouchSlop = vc.getScaledTouchSlop();
}
#Override
public void setScrollingTouchSlop(int slopConstant) {
super.setScrollingTouchSlop(slopConstant);
final ViewConfiguration vc = ViewConfiguration.get(getContext());
switch (slopConstant) {
case TOUCH_SLOP_DEFAULT:
mTouchSlop = vc.getScaledTouchSlop();
break;
case TOUCH_SLOP_PAGING:
mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(vc);
break;
default:
break;
}
}
#Override
public boolean onInterceptTouchEvent(MotionEvent e) {
final int action = MotionEventCompat.getActionMasked(e);
final int actionIndex = MotionEventCompat.getActionIndex(e);
switch (action) {
case MotionEvent.ACTION_DOWN:
mScrollPointerId = MotionEventCompat.getPointerId(e, 0);
mInitialTouchX = (int) (e.getX() + 0.5f);
mInitialTouchY = (int) (e.getY() + 0.5f);
return super.onInterceptTouchEvent(e);
case MotionEventCompat.ACTION_POINTER_DOWN:
mScrollPointerId = MotionEventCompat.getPointerId(e, actionIndex);
mInitialTouchX = (int) (MotionEventCompat.getX(e, actionIndex) + 0.5f);
mInitialTouchY = (int) (MotionEventCompat.getY(e, actionIndex) + 0.5f);
return super.onInterceptTouchEvent(e);
case MotionEvent.ACTION_MOVE: {
final int index = MotionEventCompat.findPointerIndex(e, mScrollPointerId);
if (index < 0) {
return false;
}
final int x = (int) (MotionEventCompat.getX(e, index) + 0.5f);
final int y = (int) (MotionEventCompat.getY(e, index) + 0.5f);
if (getScrollState() != SCROLL_STATE_DRAGGING) {
final int dx = x - mInitialTouchX;
final int dy = y - mInitialTouchY;
final boolean canScrollHorizontally = getLayoutManager().canScrollHorizontally();
final boolean canScrollVertically = getLayoutManager().canScrollVertically();
boolean startScroll = false;
if (canScrollHorizontally && Math.abs(dx) > mTouchSlop && (Math.abs(dx) >= Math.abs(dy) || canScrollVertically)) {
startScroll = true;
}
if (canScrollVertically && Math.abs(dy) > mTouchSlop && (Math.abs(dy) >= Math.abs(dx) || canScrollHorizontally)) {
startScroll = true;
}
return startScroll && super.onInterceptTouchEvent(e);
}
return super.onInterceptTouchEvent(e);
}
default:
return super.onInterceptTouchEvent(e);
}
}
}
For the inner horizontal RecyclerView, use this extended class:
public class FeedRootRecyclerView extends BetterRecyclerView{
public FeedRootRecyclerView(Context context) {
this(context, null);
}
public FeedRootRecyclerView(Context context, #Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public FeedRootRecyclerView(Context context, #Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
/* do nothing */
}
}
You can find proper explanation as to what these classes do over here : http://nerds.headout.com/fix-horizontal-scrolling-in-your-android-app/
I am facing problem while reading Animation scale duration flag in Marshmallow. Same code is working in earlier version of Marshmallow (6.0.0).
I am using android.app.Fragment.setCustomTransition() method for fragment animation. When Animation scale duration is 0 in developer option settings, fragments do not get displayed. So i had to disable animation over this condition.
My code snippet:
public static boolean isAnimationOff()
{
final float animatorSpeed;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1)
{
animatorSpeed = Settings.Global.getFloat(
context.getContentResolver(),
Settings.Global.ANIMATOR_DURATION_SCALE,
0);
}
else
{
animatorSpeed = Settings.System.getFloat(
context.getContentResolver(),
Settings.System.ANIMATOR_DURATION_SCALE,
0);
}
return animatorSpeed == 0;
}
The problem is :this code is returning true on each invoke even if animation scale duration is not 0.
Has anyone faced this issue?
Edit1
Following is the code snippet where I use isAnimationOff() method to load a fragment:
private void loadFragment(Fragment fragment)
{
FragmentTransaction transaction = getFragmentManager().beginTransaction();
if (!Constants.isAnimationOff())
transaction.setCustomAnimations(animSlideIn, animSlideOut);
transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE).replace(R.id.frameTopContainer, fragment)
.commitAllowingStateLoss();
}
Edit2
Here is the custom LinearLayout that has custom property:
public class FractionLinearLayout extends LinearLayout
{
DisplayMetrics matrics = getContext().getResources().getDisplayMetrics();
public FractionLinearLayout(Context context, AttributeSet attrs,
int defStyleAttr)
{
super(context, attrs, defStyleAttr);
// TODO Auto-generated constructor stub
}
public FractionLinearLayout(Context context)
{
super(context);
// TODO Auto-generated constructor stub
}
public FractionLinearLayout(Context context, AttributeSet attrs)
{
super(context, attrs);
// TODO Auto-generated constructor stub
}
public float getFractionTranslationX()
{
return getWidth() > 0 ? super.getTranslationX() / getWidth() : Float.MAX_VALUE;
}
public void setFractionTranslationX(float translationX)
{
int width = getWidth();
super.setTranslationX(width > 0 ? width * translationX : Float.MAX_VALUE);
}
public float getFractionTranslationY()
{
return getHeight() > 0 ? super.getTranslationX() / getHeight() : Float.MAX_VALUE;
}
public void setFractionTranslationY(float translationY)
{
int height = getHeight();
super.setTranslationY(height > 0 ? height * translationY : Float.MAX_VALUE);
}
public float getAnimWidth()
{
return getLayoutParams().width;
}
public void setAnimWidth(int animWidth)
{
getLayoutParams().width = animWidth;
requestLayout();
}
}
I had an issue in my FractionLinearLayout class. In set setFractionTranslationX() method, getWidth() was returning 0 at the very first call. That led my code to prevent Fragment to appear when animation is off. Now i have replaced getWidth() line with getResources().getDisplayMetrics().widthPixels and everything is working fine.
Updated Code:
public class FractionLinearLayout extends LinearLayout
{
DisplayMetrics matrics = getContext().getResources().getDisplayMetrics();
public FractionLinearLayout(Context context, AttributeSet attrs,
int defStyleAttr)
{
super(context, attrs, defStyleAttr);
// TODO Auto-generated constructor stub
}
public FractionLinearLayout(Context context)
{
super(context);
// TODO Auto-generated constructor stub
}
public FractionLinearLayout(Context context, AttributeSet attrs)
{
super(context, attrs);
// TODO Auto-generated constructor stub
}
public float getFractionTranslationX()
{
return getWidth() > 0 ? super.getTranslationX() / getWidth() : 0;
}
public void setFractionTranslationX(float translationX)
{
int width = getResources().getDisplayMetrics().widthPixels;
super.setTranslationX(width > 0 ? width * translationX : 0);
}
public float getFractionTranslationY()
{
return getHeight() > 0 ? super.getTranslationY() / getHeight() : 0;
}
public void setFractionTranslationY(float translationY)
{
int height = getResources().getDisplayMetrics().heightPixels;
super.setTranslationY(height > 0 ? height * translationY : 0);
}
public float getAnimWidth()
{
return getLayoutParams().width;
}
public void setAnimWidth(int animWidth)
{
getLayoutParams().width = animWidth;
requestLayout();
}
}
But Still I don't knowl, why is ANIMATOR_DURATION_SCALE flag not working in Marshmallow.
How can I determine the direction of a vertical ScrollView?
I need to hide and display another linear layout accoding to scrollview scroll
You can use the onScrollChanged(int l, int t, int oldl, int oldt) method for that. As there isn't a setter for a listener, but just that method, you will have to create your own ScrollView class and override that method and do your thing in your implementation.
public class ObservableScrollView extends ScrollView {
private static final int DEFAULT_THRESHOLD_DP = 4;
private ScrollDirectionListener scrollDirectionListener;
private int scrollThreshold;
public ObservableScrollView(Context context) {
this(context, null);
}
public ObservableScrollView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ObservableScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void init(Context context) {
scrollThreshold = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, DEFAULT_THRESHOLD_DP, context.getResources().getDisplayMetrics());
}
#Override
protected void onScrollChanged(int x, int y, int oldX, int oldY) {
super.onScrollChanged(x, y, oldX, oldY);
if (Math.abs(y - oldY) > scrollThreshold && scrollDirectionListener != null) {
if (y > oldY) {
scrollDirectionListener.onScrollUp(Math.abs(y - oldY));
} else {
scrollDirectionListener.onScrollDown(Math.abs(y - oldY));
}
}
}
public void setScrollThresholdInPx(int px) {
scrollThreshold = px;
}
public void setScrollThresholdInDp(Context context, float dp) {
scrollThreshold = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, context.getResources().getDisplayMetrics());
}
public void setOnScrollDirectionListener(ScrollDirectionListener listener) {
scrollDirectionListener = listener;
}
public interface ScrollDirectionListener {
void onScrollDown(int pixels);
void onScrollUp(int pixels);
}}
got answer :) use this custom scrollview
ScrollView.setOnScrollDirectionListener(new ObservableScrollView.ScrollDirectionListener() {
#Override
public void onScrollDown(int pixels) {
contenttool.setVisibility(View.INVISIBLE);
}
#Override
public void onScrollUp(int pixels) {
contenttool.setVisibility(View.VISIBLE);
}
});
Is there a way to use something similar to "View.canScrollVertically" on API 11?
http://developer.android.com/reference/android/view/View.html#canScrollVertically(int)
Thanks.
So, the implementation is simply:
public boolean canScrollVertically(int direction) {
final int offset = computeVerticalScrollOffset();
final int range = computeVerticalScrollRange() - computeVerticalScrollExtent();
if (range == 0) return false;
if (direction < 0) {
return offset > 0;
} else {
return offset < range - 1;
}
}
So you could easily just copy that into your code; however, the computeX() methods are protected. One solution here is to subclass the View and provide your own implementation:
public class CustomView extends View {
public CustomView(Context context) {
super(context);
}
public CustomView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
public boolean compatCanScrollVertically(int direction) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
return canScrollVertically(direction);
} else {
// TODO Remove when minSdkVersion >= 14
final int offset = computeVerticalScrollOffset();
final int range = computeVerticalScrollRange() - computeVerticalScrollExtent();
if (range == 0) return false;
if (direction < 0) {
return offset > 0;
} else {
return offset < range - 1;
}
}
}
}
I looked over the SwipeRefreshLayout codes, the view has the same need for a view that whether can scroll in the specified direction.
/**
* #return Whether it is possible for the child view of this layout to
* scroll up. Override this if the child view is a custom view.
*/
public boolean canChildScrollUp() {
if (android.os.Build.VERSION.SDK_INT < 14) {
if (mTarget instanceof AbsListView) {
final AbsListView absListView = (AbsListView) mTarget;
return absListView.getChildCount() > 0
&& (absListView.getFirstVisiblePosition() > 0 || absListView.getChildAt(0)
.getTop() < absListView.getPaddingTop());
} else {
return mTarget.getScrollY() > 0;
}
} else {
return ViewCompat.canScrollVertically(mTarget, -1);
}
}
scrollDown also use same manner.
I am looking for any good solution to make parallax with two Views.
My first view is Fragment and second is ListView.
Fragment have min height equals 200dp and when I want drag listview to down the fragment will grow.
I wrote some code, but it isn't all I need.
public class MyListView extends ListView implements OnTouchListener {
int touchActionDownY, touchActionMoveY;
int parentHeight;
int initialPosition = -1;
TextView viewToMove;
LinearLayout.LayoutParams params;
public MyListView(Context context) {
super(context);
init();
}
public MyListView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public MyListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init () {
setOnTouchListener(this);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
parentHeight = MeasureSpec.getSize(heightMeasureSpec);
initialPosition = (int) getY();
}
#Override
public boolean onTouch(View v, MotionEvent event) {
int deltaY = 0;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
touchActionDownY = (int)event.getY();
touchActionMoveY = touchActionDownY;
break;
case MotionEvent.ACTION_POINTER_UP:
break;
case MotionEvent.ACTION_MOVE:
deltaY = (int)event.getY() - touchActionMoveY;
touchActionMoveY = (int)event.getY();
if(getFirstVisiblePosition() == 0 && v.getY() < parentHeight && touchActionMoveY > touchActionDownY && deltaY > 0){
params.height = params.height + deltaY;
viewToMove.setLayoutParams(params);
return true;
} else if(v.getY() > 200 && touchActionMoveY < touchActionDownY && deltaY < 0) {
params.height = params.height + deltaY;;
viewToMove.setLayoutParams(params);
return true;
} else {
}
break;
}
return false;
}
public void setViewToMove(TextView v) {
viewToMove = v;
params = (android.widget.LinearLayout.LayoutParams) viewToMove.getLayoutParams();
}
}
In this ListView I need smooth view scroll when user throw list. (View stoped, because method work with ACTION_MOVE).
Any help?
I need interaction with my first fragment.
Parallax should work when only list is draged.