I'm creating a Scrolling Activity in my Android App. The Activity has a CollapsingToolbarLayout with parallax effect.
When I scroll the layout below the appbarlayout up, it'll go up smoothly and the appbarlayout will be collapsed up to the title.The ImageView and the TextView will go up to the title. And when I scroll the layout back down, they'll all go back down to the beginning.
The bug is here:
when I running the activity on some devices, sometimes when I scroll it up, the layout will be stucked there up and down for seconds and then, back go to the top.
And when I running the activity on some other devices, it'll be OK, nothing wrong happened.
The demo of this bug: https://share.weiyun.com/1d797a4a92580e1595eacb226f9a92a3
Here is the layout:
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
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="match_parent"
tools:context="nczz.cn.helloworld.ScrollingActivity"
>
<android.support.design.widget.AppBarLayout
android:id="#+id/app_bar"
android:layout_width="match_parent"
android:layout_height="#dimen/app_bar_height"
android:background="#FA7199"
app:layout_scrollFlags="scroll|enterAlways"
android:theme="#style/AppTheme.AppBarOverlay">
<nczz.cn.widget.CollapsingImageTextLayout
android:id="#+id/imageTextLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_scrollFlags="scroll|exitUntilCollapsed"
app:title_id="#+id/test_title"
app:text_id="#+id/test_text"
app:img_scale="0.6"
app:text_scale="0.6"
app:text_margin_left="110dp"
app:img_id="#+id/test_img"
app:img_margin_left="55dp"
>
<LinearLayout
android:id="#+id/test_title"
android:layout_width="match_parent"
android:layout_height="80dp"
android:background="#FA7199"
android:gravity="center_vertical"
android:orientation="horizontal"
>
<ImageView
android:id="#+id/return_btn"
android:layout_width="15dp"
android:layout_height="15dp"
android:layout_marginLeft="20dp"
android:layout_centerVertical="true"
android:src="#drawable/left" />
</LinearLayout>
<ImageView
android:id="#+id/test_img"
android:layout_width="80dp"
android:layout_height="80dp"
android:scaleType="fitXY"
android:src="#mipmap/ic_launcher"
android:layout_centerInParent="true"
android:layout_marginBottom="30dp"
/>
<TextView
android:id="#+id/test_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/test_img"
android:text="MoveText"
android:textSize="20sp"
android:textColor="#android:color/white"
android:layout_marginTop="-20dp"
android:layout_marginLeft="50dp"
android:layout_centerInParent="true"
/>
</nczz.cn.widget.CollapsingImageTextLayout>
</android.support.design.widget.AppBarLayout>
<include
android:id="#+id/includelayout"
layout="#layout/content_scrolling"/>
</android.support.design.widget.CoordinatorLayout>
Here is the CollapsingImageTextLayout:
package nczz.cn.widget;
import android.content.Context;
import android.content.res.TypedArray;
import android.support.annotation.NonNull;
import android.support.design.widget.AppBarLayout;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.widget.RelativeLayout;
import nczz.cn.helloworld.R;
/**
* Created by yahui.hu on 2017/4/21.
*/
public class CollapsingImageTextLayout extends RelativeLayout {
private AppBarLayout.OnOffsetChangedListener mOffsetChangedListener;
private int mTitleId, mTextId, mImageId;
private int mTitleMarginLeft, mTitleMarginTop, mImgMarginLeft, mImgMarginTop;
private float mTextScale, mImgScale;
private View mTitle, mImg, mText;
private boolean isGetView = true;
private int mTitleHeight = 0;
public CollapsingImageTextLayout(Context context) {
this(context, null);
}
public CollapsingImageTextLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CollapsingImageTextLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.CollapsingImageLayout, defStyleAttr, 0);
mTitleId = a.getResourceId(R.styleable.CollapsingImageLayout_title_id, 0);
mTextId = a.getResourceId(R.styleable.CollapsingImageLayout_text_id, 0);
mImageId = a.getResourceId(R.styleable.CollapsingImageLayout_img_id, 0);
mTextScale = a.getFloat(R.styleable.CollapsingImageLayout_text_scale, 0.4f);
mImgScale = a.getFloat(R.styleable.CollapsingImageLayout_img_scale, 0.4f);
mTitleMarginLeft = a.getDimensionPixelSize(R.styleable.CollapsingImageLayout_text_margin_left, 0);
mTitleMarginTop = a.getDimensionPixelSize(R.styleable.CollapsingImageLayout_text_margin_top, 0);
mImgMarginLeft = a.getDimensionPixelSize(R.styleable.CollapsingImageLayout_img_margin_left, 0);
mImgMarginTop = a.getDimensionPixelSize(R.styleable.CollapsingImageLayout_img_margin_top, 0);
a.recycle();
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
getView();
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
private void getView() {
if (!isGetView) {
return;
}
if (mTitleId != 0) {
mTitle = findViewById(mTitleId);
}
if (mTextId != 0) {
mText = findViewById(mTextId);
}
if (mImageId != 0) {
mImg = findViewById(mImageId);
}
isGetView = false;
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
if (mTitle != null) {
getViewOffsetHelper(mTitle).onViewLayout(0, 0);
setMinimumHeight(getHeightWithMargins(mTitle));
mTitleHeight = mTitle.getHeight();
this.bringChildToFront(mTitle);
}
if (mImg != null) {
getViewOffsetHelper(mImg).onViewLayout(mImgMarginLeft, mImgMarginTop);
this.bringChildToFront(mImg);
}
if (mText != null) {
getViewOffsetHelper(mText).onViewLayout(mTitleMarginLeft, mTitleMarginTop);
this.bringChildToFront(mText);
}
}
static ViewHelper getViewOffsetHelper(View view) {
ViewHelper offsetHelper = (ViewHelper) view.getTag(R.id.view_helper);
if (offsetHelper == null) {
offsetHelper = new ViewHelper(view);
view.setTag(R.id.view_helper, offsetHelper);
}
return offsetHelper;
}
private static int getHeightWithMargins(#NonNull final View view) {
final ViewGroup.LayoutParams lp = view.getLayoutParams();
if (lp instanceof MarginLayoutParams) {
final MarginLayoutParams mlp = (MarginLayoutParams) lp;
return view.getHeight() + mlp.topMargin + mlp.bottomMargin;
}
return view.getHeight();
}
#Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
ViewParent viewParent = getParent();
if (viewParent instanceof AppBarLayout) {
if (mOffsetChangedListener == null) mOffsetChangedListener = new OffsetListenerImp();
((AppBarLayout) viewParent).addOnOffsetChangedListener(mOffsetChangedListener);
}
}
#Override
protected void onDetachedFromWindow() {
ViewParent viewParent = getParent();
if (viewParent instanceof AppBarLayout) {
((AppBarLayout) viewParent).removeOnOffsetChangedListener(mOffsetChangedListener);
}
super.onDetachedFromWindow();
}
final int getMaxOffsetForPinChild(View child) {
final ViewHelper offsetHelper = getViewOffsetHelper(child);
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
return getHeight()
- offsetHelper.getLayoutTop()
- child.getHeight()
- lp.bottomMargin;
}
static int constrain(int amount, int low, int high) {
return amount < low ? low : (amount > high ? high : amount);
}
static int constrain(int amount, int low) {
return amount < low ? low : amount;
}
private void setTopAndBottomOffset(View child, int verticalOffset) {
ViewHelper viewHelper = (ViewHelper) child.getTag(R.id.view_helper);
viewHelper.setTopAndBottomOffset(
constrain(-verticalOffset, 0, getMaxOffsetForPinChild(child)));
Log.e("setTopAndBottomOffset",""+-verticalOffset);
}
private void setTopAndBottomOffset(View child, int verticalOffset, float scale) {
ViewHelper viewHelper = (ViewHelper) child.getTag(R.id.view_helper);
viewHelper.setTopAndBottomOffset(
constrain(-verticalOffset - getMaxOffset(viewHelper, scale),
0));
//Log.e("setTopAndBottomOffset",""+-verticalOffset);
}
private void setLeftAndRightOffset(View child, int verticalOffset, float scale) {
ViewHelper viewHelper = (ViewHelper) child.getTag(R.id.view_helper);
int maxOffsetDistance = getMaxOffset(viewHelper, scale);
int maxLeft = viewHelper.getLayoutLeft()
+ (viewHelper.getViewWidth() - viewHelper.getScaleViewWidth(scale))
- viewHelper.getMarginTitleLeft();
int realOffset = (int) (maxLeft * 1.0f / (maxOffsetDistance * 1.0f) * verticalOffset);
realOffset = constrain(realOffset, -maxLeft, maxLeft);
viewHelper.setLeftAndRightOffset(realOffset);
// Log.e("setLeftAndRightOffset",""+realOffset);
}
private void setViewScale(View child, int verticalOffset, float scale) {
ViewHelper viewHelper = (ViewHelper) child.getTag(R.id.view_helper);
int maxOffsetDistance = getMaxOffset(viewHelper, scale);
float realScale = -verticalOffset - maxOffsetDistance > 0 ? scale : verticalOffset == 0 ? 1f : 0f;
if (realScale == 0) {
realScale = (maxOffsetDistance + verticalOffset * (1 - scale)) / (maxOffsetDistance * 1f);
}
viewHelper.setViewOffsetScale(realScale);
}
private int getMaxOffset(ViewHelper viewHelper, float scale) {
int scaleViewHeight = (int) (scale * viewHelper.getViewHeight());
int offsetTitleDistance = scaleViewHeight >= mTitleHeight ? 0 : (mTitleHeight - scaleViewHeight) / 2;
int marginTop = viewHelper.getMarginTitleTop() >= offsetTitleDistance ? offsetTitleDistance : viewHelper.getMarginTitleTop();
return viewHelper.getLayoutBottom() - viewHelper.getScaleViewHeight(scale) - offsetTitleDistance - marginTop;
}
private class OffsetListenerImp implements AppBarLayout.OnOffsetChangedListener {
#Override
public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
if (mTitle != null) {
setTopAndBottomOffset(mTitle, verticalOffset);
}
if (mText != null) {
setTopAndBottomOffset(mText, verticalOffset, mTextScale);
setLeftAndRightOffset(mText, verticalOffset, mTextScale);
setViewScale(mText, verticalOffset, mTextScale);
}
if (mImg != null) {
setTopAndBottomOffset(mImg, verticalOffset, mImgScale);
setLeftAndRightOffset(mImg, verticalOffset, mImgScale);
setViewScale(mImg, verticalOffset, mImgScale);
}
}
}
public void setImgTitleMarginTop(int top) {
if (mImg != null) {
getViewOffsetHelper(mImg).setMarginTitleTop(top);
}
}
public void setImgTitleMarginLeft(int left) {
if (mImg != null) {
getViewOffsetHelper(mImg).setMarginTitleLeft(left);
}
}
public void setTextTitleMarginTop(int top) {
if (mText != null) {
getViewOffsetHelper(mText).setMarginTitleTop(top);
}
}
public void setImgTextMarginLeft(int left) {
if (mText != null) {
getViewOffsetHelper(mText).setMarginTitleLeft(left);
}
}
}
Here is the content_scolling.xml:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.NestedScrollView
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:background="#cccccc"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
tools:context="nczz.cn.helloworld.ScrollingActivity"
tools:showIn="#layout/activity_scrolling">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/large_text" />
</android.support.v4.widget.NestedScrollView>
Here is the java:
package nczz.cn.helloworld;
import android.app.Activity;
import android.content.Intent;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.widget.LinearLayout;
public class ScrollingActivity extends Activity {
LinearLayout titleTxt;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_scrolling);
initViews();
setTitleBarHeight();
}
private void initViews(){
titleTxt= (LinearLayout) findViewById(R.id.test_title);
}
private void setTitleBarHeight(){
WindowManager manager=getWindowManager();
int height=manager.getDefaultDisplay().getHeight();
ViewGroup.LayoutParams params=titleTxt.getLayoutParams();
params.height=height/12;
titleTxt.setLayoutParams(params);
}
}
i am not sure but u can use following code according to view inside nested scrollview
viewlayoutInsidescrollview.setNestedScrollingEnabled(false);
in java class
Related
I am facing 1 issue with BottomSheet in Android. What I am trying to achieve :
BottomSheet Google Direction
But I am not able to implement ListView with header and footer like google.
The main issue is ListView shows all the items and my bottom sheet not displaying footer view. I want to always visible the bottom view of my bottom sheet.
If the footer is your main problem here's a simple solution:
Add the footer View as a direct child of your BottomSheet
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="#+id/bottom_sheet"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#EECCCCCC"
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
<TextView
android:id="#+id/pinned_bottom"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#500000FF"
android:padding="16dp"
android:text="Bottom" />
</FrameLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
Add a BottomSheetCallback and adjust the footer's translationY in onSlide()
BottomSheetBehavior.from(bottom_sheet).addBottomSheetCallback(
object : BottomSheetBehavior.BottomSheetCallback() {
override fun onSlide(bottomSheet: View, slideOffset: Float) {
val bottomSheetVisibleHeight = bottomSheet.height - bottomSheet.top
pinned_bottom.translationY =
(bottomSheetVisibleHeight - pinned_bottom.height).toFloat()
}
override fun onStateChanged(bottomSheet: View, newState: Int) {
}
})
It runs smoothly because you're simply changing the translationY.
You can also use this technique to pin a View in the center of the BottomSheet:
pinned_center.translationY = (bottomSheetVisibleHeight - pinned_center.height) / 2f
I've made a sample project on GitHub to reproduce both use cases (pinned to center and bottom): https://github.com/dadouf/BottomSheetGravity
I have created sample example to achieve bottom sheet as google direction that will floating
on any ui and working will be same as google direction swipable view. I achieved it using
"ViewDragHelper" class(https://developer.android.com/reference/android/support/v4/widget/ViewDragHelper)
Here is the sample what i achieved with "ViewDragHelper" same as google direction swipable view:
Note: In below example, there is hard coded strings as well a single
adapter taken only in swipable view class and also taken static list.
Anyone can customize it with proper code format as well
getter/setters. This is for example only to taught how
"ViewDragHelper" works.
First create "GoogleBottomSheet" class as below:
import android.content.Context;
import android.support.v4.view.MotionEventCompat;
import android.support.v4.view.ViewCompat;
import android.support.v4.widget.ViewDragHelper;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import java.util.ArrayList;
public class GoogleBottomSheet extends ViewGroup {
private final ViewDragHelper mDragHelper;
GoogleRoutesAdapter googleRoutesAdapter;
private View mHeaderView;
private RecyclerView rvList;
private float mInitialMotionX;
private float mInitialMotionY;
private int mDragRange;
private int mTop;
private float mDragOffset;
public GoogleBottomSheet(Context context) {
this(context, null);
}
public GoogleBottomSheet(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
#Override
protected void onFinishInflate() {
super.onFinishInflate();
mHeaderView = findViewById(R.id.viewHeader);
rvList = findViewById(R.id.rvList);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getContext(), LinearLayoutManager.VERTICAL, false);
rvList.setLayoutManager(linearLayoutManager);
ArrayList<String> allRoutesList = new ArrayList<>();
allRoutesList.add("47 Bourbon Li");
allRoutesList.add("Head South");
allRoutesList.add("Princess Street");
allRoutesList.add("A 3-lane partially one-way street heading out of Manchester city centre");
allRoutesList.add("Manchester Jewish Museum, \n" +
"Peninsula Building");
allRoutesList.add("Portland Street");
allRoutesList.add("Quay Street");
allRoutesList.add("Forms part of the city's historic Northern Quarter district");
allRoutesList.add("Sackville Street Building, University of Manchester including the Godlee Observatory");
allRoutesList.add("Turn Left on S Naper");
allRoutesList.add("150 W-Stall");
allRoutesList.add("Former National Westminster Bank");
allRoutesList.add("Bradshaw, L. D.");
allRoutesList.add("House of Commons Transport Committee");
allRoutesList.add("A street only for Metrolink trams and previously buses which joined the street at Lower Mosley Street.");
googleRoutesAdapter = new GoogleRoutesAdapter(getContext(), allRoutesList);
rvList.setAdapter(googleRoutesAdapter);
}
public GoogleBottomSheet(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mDragHelper = ViewDragHelper.create(this, 1f, new DragHelperCallback());
}
public void maximize() {
smoothSlideTo(0f);
}
boolean smoothSlideTo(float slideOffset) {
final int topBound = getPaddingTop();
int y = (int) (topBound + slideOffset * mDragRange);
if (mDragHelper.smoothSlideViewTo(mHeaderView, mHeaderView.getLeft(), y)) {
ViewCompat.postInvalidateOnAnimation(this);
return true;
}
return false;
}
private class DragHelperCallback extends ViewDragHelper.Callback {
#Override
public boolean tryCaptureView(View child, int pointerId) {
return child == mHeaderView;
}
#Override
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
mTop = top;
mDragOffset = (float) top / mDragRange;
requestLayout();
}
#Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
int top = getPaddingTop();
if (yvel > 0 || (yvel == 0 && mDragOffset > 0.5f)) {
top += mDragRange;
}
mDragHelper.settleCapturedViewAt(releasedChild.getLeft(), top);
}
#Override
public int getViewVerticalDragRange(View child) {
return mDragRange;
}
#Override
public int clampViewPositionVertical(View child, int top, int dy) {
final int topBound = getPaddingTop();
final int bottomBound = getHeight() - mHeaderView.getHeight();
final int newTop = Math.min(Math.max(top, topBound), bottomBound);
return newTop;
}
}
#Override
public void computeScroll() {
if (mDragHelper.continueSettling(true)) {
ViewCompat.postInvalidateOnAnimation(this);
}
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
final int action = MotionEventCompat.getActionMasked(ev);
if ((action != MotionEvent.ACTION_DOWN)) {
mDragHelper.cancel();
return super.onInterceptTouchEvent(ev);
}
if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
mDragHelper.cancel();
return false;
}
final float x = ev.getX();
final float y = ev.getY();
boolean interceptTap = false;
switch (action) {
case MotionEvent.ACTION_DOWN: {
mInitialMotionX = x;
mInitialMotionY = y;
interceptTap = mDragHelper.isViewUnder(mHeaderView, (int) x, (int) y);
break;
}
case MotionEvent.ACTION_MOVE: {
final float adx = Math.abs(x - mInitialMotionX);
final float ady = Math.abs(y - mInitialMotionY);
final int slop = mDragHelper.getTouchSlop();
if (ady > slop && adx > ady) {
mDragHelper.cancel();
return false;
}
}
}
return mDragHelper.shouldInterceptTouchEvent(ev) || interceptTap;
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
mDragHelper.processTouchEvent(ev);
final int action = ev.getAction();
final float x = ev.getX();
final float y = ev.getY();
boolean isHeaderViewUnder = mDragHelper.isViewUnder(mHeaderView, (int) x, (int) y);
switch (action & MotionEventCompat.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
mInitialMotionX = x;
mInitialMotionY = y;
break;
}
case MotionEvent.ACTION_UP: {
final float dx = x - mInitialMotionX;
final float dy = y - mInitialMotionY;
final int slop = mDragHelper.getTouchSlop();
if (dx * dx + dy * dy < slop * slop && isHeaderViewUnder) {
if (mDragOffset == 0) {
smoothSlideTo(1f);
} else {
smoothSlideTo(0f);
}
}
break;
}
}
return isHeaderViewUnder && isViewHit(mHeaderView, (int) x, (int) y) ||
isViewHit(rvList, (int) x, (int) y);
}
private boolean isViewHit(View view, int x, int y) {
int[] viewLocation = new int[2];
view.getLocationOnScreen(viewLocation);
int[] parentLocation = new int[2];
this.getLocationOnScreen(parentLocation);
int screenX = parentLocation[0] + x;
int screenY = parentLocation[1] + y;
return screenX >= viewLocation[0] && screenX < viewLocation[0] + view.getWidth() &&
screenY >= viewLocation[1] && screenY < viewLocation[1] + view.getHeight();
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
measureChildren(widthMeasureSpec, heightMeasureSpec);
int maxWidth = MeasureSpec.getSize(widthMeasureSpec);
int maxHeight = MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, 0),
resolveSizeAndState(maxHeight, heightMeasureSpec, 0));
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
mDragRange = getHeight() - mHeaderView.getHeight();
mHeaderView.layout(
0,
mTop,
r,
mTop + mHeaderView.getMeasuredHeight());
rvList.layout(
0,
mTop + mHeaderView.getMeasuredHeight(),
r,
mTop + b);
}
}
Create xml file named as "rawitem_mapdetails.xml" for recyclerview viewholder item as below:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/mTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/colorPrimaryDark"
android:text="Route 1"
android:padding="#dimen/_10sdp"
android:textColor="#android:color/white"
android:textSize="#dimen/_15sdp" />
</RelativeLayout>
</LinearLayout>
Create simple adapter named "GoogleRoutesAdapter" for recyclerview as below:
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;
import java.util.ArrayList;
public class GoogleRoutesAdapter extends RecyclerView.Adapter<GoogleRoutesAdapter.GoogleRouteViewHolder> {
private Context mContext;
private ArrayList<String> allRoutesList;
public GoogleRoutesAdapter(Context context, ArrayList<String> allRoutesList) {
this.mContext = context;
this.allRoutesList = allRoutesList;
}
#Override
public GoogleRouteViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View layoutView = LayoutInflater.from(parent.getContext()).inflate(R.layout.rawitem_mapdetails, null);
GoogleRouteViewHolder rcv = new GoogleRouteViewHolder(layoutView);
return rcv;
}
#Override
public void onBindViewHolder(final GoogleRouteViewHolder holder, final int position) {
holder.tvRoute.setText(allRoutesList.get(position));
}
#Override
public int getItemCount() {
return allRoutesList.size();
}
public class GoogleRouteViewHolder extends RecyclerView.ViewHolder {
private TextView tvRoute;
public GoogleRouteViewHolder(View view) {
super(view);
tvRoute = view.findViewById(R.id.mTextView);
tvRoute.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Toast.makeText(mContext, allRoutesList.get(getAdapterPosition()), Toast.LENGTH_SHORT).show();
}
});
}
}
}
Create "activity_main.xml" as below for MainActivity as below:
<?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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.viewdraghelper.GoogleBottomSheet
android:id="#+id/my_googleBottomSheet"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/viewHeader"
android:layout_width="match_parent"
android:background="#color/colorAccent"
android:layout_height="#dimen/_80sdp"
android:textSize="#dimen/_25sdp"
android:padding="#dimen/_10sdp"
android:textColor="#android:color/white"
android:text="31 min (29 mi)"/>
<android.support.v7.widget.RecyclerView
android:id="#+id/rvList"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</com.viewdraghelper.GoogleBottomSheet>
</RelativeLayout>
Edited Answer based on requirements as below:
1. To get sliding panel at bottom/hidden as default state on view created first time
First take initOnce global boolean variable
private boolean initOnce = false;
Then change onLayout() method as below:
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
if(!initOnce){
initOnce = true;
mDragRange = getHeight() - mHeaderView.getHeight();
mHeaderView.layout(
0,
b - mHeaderView.getMeasuredHeight(),
r,
b);
}else {
mDragRange = getHeight() - mHeaderView.getHeight();
mHeaderView.layout(
0,
mTop,
r,
mTop + mHeaderView.getMeasuredHeight());
rvList.layout(
0,
mTop + mHeaderView.getMeasuredHeight(),
r,
mTop + b);
}
}
Now all done! As i stated above that this is to only taught how "ViewDragHelper" works thats why we don't
have to do anything in MainActivity right now because all adapter logic resides in "GoogleBottomSheet" class.
I have also take one simple recyclerview item click so anyone can have better idea that other ui will work same
as its own behaviour. We can also customize by putting any ui in "GoogleBottomSheet".
Hope it helps! Happy Coding :)
I am working on a project where I need to design two vertical sliders as shown in the image below
I had developed code which working fine in a range of 0 to 100.
But I am not sure of how to convert it to 18 to 80
Here I am adding my class and view XML
any help is appreciated.
Following is my Layout
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.os.Build;
import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.RelativeLayout;
import android.widget.TextView;
import wedviser.com.wedviser.R;
/**
* Created by Focaloid on 29-12-2016.
*/
public class RangeBarVerticalAge extends RelativeLayout {
private static final int TOTAL_DIVISION_COUNT = 100;
private static final int MAX_CLICK_DURATION = 200;
public OnRangeBarChangeListener onRangeBarChangeListener;
private int inactiveColor;
private int activeColor;
private double heightParent;
private View viewFilterMain, viewThumbMin, viewThumbMax;
private RelativeLayout relFilterMin, relFilterMax;
private float startYMin, startYMax;
private float movedYMin, movedYMax;
private int initialHeightMin;
private float dTopMin, dTopMax;
private int currentHeightMin, currentHeightMax;
private double resultMin = 0.0;
private double resultMax = 100.0;
private View viewParent;
// private TextView tvFilterMin, tvFilterMax;
private Context context;
private long startClickTime;
private RelativeLayout relativeLayout;
private int minRange = 0, maxRange = 100;
private View viewInActiveTop, viewInActiveBottom;
public RangeBarVerticalAge(Context context) {
super(context);
this.context = context;
initialize(context);
}
public RangeBarVerticalAge(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RangeBarVerticalAge, 0, 0);
System.out.println(a.getIndexCount());
activeColor = a.getColor(R.styleable.RangeBarVerticalAge_activeColor, Color.parseColor("#007FFF"));
inactiveColor = a.getColor(R.styleable.RangeBarVerticalAge_inactiveColor, Color.parseColor("#808080"));
a.recycle();
initialize(context);
}
public RangeBarVerticalAge(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.context = context;
initialize(context);
}
public static float convertDpToPixel(float dp, Context context) {
Resources resources = context.getResources();
DisplayMetrics metrics = resources.getDisplayMetrics();
return dp * ((float) metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT);
}
private void initialize(Context context) {
inflate(context, R.layout.rangebar_age, this);
onRangeBarChangeListener = (OnRangeBarChangeListener) context;
onRangeBarChangeListener.onRangeBarChangeAge((int) resultMin, (int) resultMax);
relativeLayout = (RelativeLayout) findViewById(R.id.rel_main);
// tvFilterMin = (TextView) findViewById(R.id.tv_filter_min);
//tvFilterMax = (TextView) findViewById(R.id.tv_filter_max);
relFilterMin = (RelativeLayout) findViewById(R.id.rel_filter_min);
relFilterMax = (RelativeLayout) findViewById(R.id.rel_filter_max);
viewThumbMax = findViewById(R.id.oval_thumb_max);
viewThumbMin = findViewById(R.id.oval_thumb_min);
viewFilterMain = findViewById(R.id.filter_main_view);
viewParent = findViewById(R.id.view_filter_parent);
viewInActiveTop = findViewById(R.id.view_inactive_line_top);
viewInActiveBottom = findViewById(R.id.view_inactive_line_bottom);
init();
relFilterMin.setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
startYMin = event.getRawY();
// startClickTime = Calendar.getInstance().getTimeInMillis();
break;
case MotionEvent.ACTION_UP: {
// long clickDuration = Calendar.getInstance().getTimeInMillis() - startClickTime;
// if (clickDuration < MAX_CLICK_DURATION) {
// //Click event triggered
//
// }
break;
}
case MotionEvent.ACTION_MOVE:
movedYMin = event.getRawY() - startYMin;
startYMin = event.getRawY();
if (v.getHeight() + movedYMin <= initialHeightMin || dTopMin + v.getHeight() + movedYMin >= dTopMax) {
currentHeightMin = v.getHeight();
getResultMin();
break;
}
ViewGroup.LayoutParams layoutParams = v.getLayoutParams();
layoutParams.height += movedYMin;
v.setLayoutParams(layoutParams);
dTopMin = v.getY();
currentHeightMin = v.getHeight();
getResultMin();
break;
default:
return false;
}
return true;
}
});
relFilterMax.setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
startYMax = event.getRawY();
break;
case MotionEvent.ACTION_MOVE:
movedYMax = event.getRawY() - startYMax;
startYMax = event.getRawY();
if (v.getHeight() - movedYMax <= initialHeightMin || v.getY() + movedYMax <= currentHeightMin + dTopMin) {
currentHeightMax = v.getHeight();
getResultMax();
break;
}
ViewGroup.LayoutParams layoutParams = v.getLayoutParams();
layoutParams.height -= movedYMax;
v.setLayoutParams(layoutParams);
dTopMax = v.getY();
currentHeightMax = v.getHeight();
getResultMax();
break;
default:
return false;
}
return true;
}
});
}
private void init() {
// ViewCompat.setElevation(tvFilterMin, 100f);
viewFilterMain.setBackgroundColor(activeColor);
viewInActiveBottom.setBackgroundColor(inactiveColor);
viewInActiveTop.setBackgroundColor(inactiveColor);
initialHeightMin = (int) convertDpToPixel(30, context);
final ViewTreeObserver viewTreeObserver = relativeLayout.getViewTreeObserver();
// if (viewTreeObserver.isAlive())
viewTreeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
public void onGlobalLayout() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
viewParent.getViewTreeObserver().removeOnGlobalLayoutListener(this);
} else {
viewParent.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
dTopMin = relFilterMin.getY();
dTopMax = relFilterMax.getY();
currentHeightMin = relFilterMin.getHeight();
System.out.println("viewParentGetHeight:" + viewParent.getHeight());
heightParent = viewParent.getHeight() - 2 * initialHeightMin;
}
});
}
public void getResultMin() {
//Max
resultMin = Math.floor(100 * (Math.abs(currentHeightMin - initialHeightMin)) / heightParent);
//tvFilterMin.setText((int) resultMin + "");
onRangeBarChangeListener.onRangeBarChangeAge((int) resultMin, (int) resultMax);
}
public void getResultMax() {
resultMax = Math.floor(100 * (Math.abs(currentHeightMax - initialHeightMin)) / heightParent);
resultMax = Math.abs(resultMax - 100);
//tvFilterMax.setText(((int) resultMax + ""));
onRangeBarChangeListener.onRangeBarChangeAge((int) resultMin, (int) resultMax);
}
public int getMinimumProgress() {
return (int) resultMin;
}
public void setMinimumProgress(final int minProgress) {
if (minProgress >= 0 && minProgress < 100 && minProgress < resultMax) {
resultMin = minProgress;
viewParent.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
viewParent.getViewTreeObserver().removeOnGlobalLayoutListener(this);
} else {
viewParent.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
currentHeightMin = ((minProgress * (viewParent.getHeight() - 2 * initialHeightMin) / 100) + initialHeightMin);
ViewGroup.LayoutParams layoutParams = relFilterMin.getLayoutParams();
layoutParams.height = currentHeightMin;
relFilterMin.setLayoutParams(layoutParams);
}
});
//tvFilterMin.setText((int) resultMin + "");
onRangeBarChangeListener.onRangeBarChangeAge((int) resultMin, (int) resultMax);
}
}
public int getMaximumProgress() {
return (int) resultMax;
}
public void setMaximumProgress(final int maxProgress) {
if (maxProgress >= 0 && maxProgress <= 100 && maxProgress > resultMin) {
resultMax = maxProgress;
viewParent.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
viewParent.getViewTreeObserver().removeOnGlobalLayoutListener(this);
} else {
viewParent.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
currentHeightMax = ((Math.abs(maxProgress - 100) * (viewParent.getHeight() - 2 * initialHeightMin) / 100) + initialHeightMin);
ViewGroup.LayoutParams layoutParams = relFilterMax.getLayoutParams();
layoutParams.height = currentHeightMax;
relFilterMax.setLayoutParams(layoutParams);
}
});
// tvFilterMax.setText((int) resultMax + "");
onRangeBarChangeListener.onRangeBarChangeAge((int) resultMin, (int) resultMax);
}
}
public interface OnRangeBarChangeListener {
void onRangeBarChangeAge(int min, int max);
}
}
Folowing is my XML
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/rel_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#android:color/transparent"
android:orientation="vertical"
android:animateLayoutChanges="true"
>
<View
android:id="#+id/view_filter_parent"
android:layout_width="1dp"
android:layout_height="match_parent"
android:background="#android:color/transparent"
android:clickable="false" />
<View
android:id="#+id/filter_main_view"
android:layout_width="2dp"
android:layout_height="match_parent"
android:layout_centerInParent="true"
android:layout_marginBottom="20dp"
android:layout_alignRight="#+id/rel_filter_min"
android:layout_alignEnd="#+id/rel_filter_min"
android:layout_marginRight="11dp"
android:layout_marginEnd="11dp"
android:background="#2196f3" />
<RelativeLayout
android:id="#+id/rel_filter_min"
android:layout_width="wrap_content"
android:layout_height="30dp"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_gravity="bottom"
>
<RelativeLayout
android:id="#+id/rel_filter_min_text"
android:layout_alignParentBottom="true"
android:layout_marginEnd="10dp"
android:layout_marginRight="10dp"
android:background="#android:color/transparent"
android:layout_alignParentStart="true"
android:layout_alignParentLeft="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"></RelativeLayout>
<View
android:id="#+id/view_inactive_line_top"
android:layout_width="2dp"
android:layout_height="match_parent"
android:layout_marginTop="5dp"
android:layout_alignLeft="#+id/oval_thumb_min"
android:layout_alignStart="#+id/oval_thumb_min"
android:layout_marginLeft="8.5dp"
android:layout_marginStart="8.5dp"
android:layout_marginBottom="10dp"
android:background="#808080" />
<View
android:id="#+id/oval_thumb_min"
android:layout_width="22dp"
android:layout_height="22dp"
android:layout_toRightOf="#+id/rel_filter_min_text"
android:layout_toEndOf="#+id/rel_filter_min_text"
android:layout_centerInParent="true"
android:layout_alignParentBottom="true"
android:layout_marginBottom="4dp"
android:background="#drawable/oval_shape"
/>
</RelativeLayout>
<RelativeLayout
android:id="#+id/rel_filter_max"
android:layout_width="wrap_content"
android:layout_height="30dp"
android:layout_alignLeft="#+id/filter_main_view"
android:layout_alignParentBottom="true"
android:layout_alignStart="#+id/filter_main_view"
android:layout_gravity="bottom"
android:layout_marginLeft="-11dp"
android:layout_marginStart="-11dp">
<View
android:id="#+id/view_inactive_line_bottom"
android:layout_width="2dp"
android:layout_height="match_parent"
android:layout_marginBottom="10dp"
android:layout_marginLeft="10.5dp"
android:layout_marginStart="10.5dp"
android:layout_marginTop="15dp"
android:background="#808080" />
<View
android:id="#+id/oval_thumb_max"
android:layout_width="22dp"
android:layout_height="22dp"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:background="#drawable/oval_shape"
/>
</RelativeLayout>
</RelativeLayout>
In my android application I have a recyclerview and LinearLayout. LinearLayout lies on the top and recyclerview lies down to the linearlayout. what my requirement is when recyclerview starts scrolls the last item, the linearlayout should start hide in propotion to scroll amount.
I have done a little and it works. but it is not proportional to the scroll amount. here is my code
ScrollListener
public class MyScrollListener extends OnScrollListener {
private LinearLayoutManager mLayoutManager;
public MyScrollListener(LinearLayoutManager manager) {
this.mLayoutManager = manager;
}
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
int visibleItemCount = mLayoutManager.getChildCount();
int totalItemCount = mLayoutManager.getItemCount();
int firstVisibleItem = mLayoutManager.findFirstVisibleItemPosition();
if (visibleItemCount + firstVisibleItem >= totalItemCount) {
scrollTheShit(dy * -1, ((View) recyclerView.getChildAt(visibleItemCount - 1)).getWidth());
}
}
public void scrollTheShit(int dy, int widthOfLastChild) {}
}
MainActivity
public class MainActivity extends AppCompatActivity {
RecyclerView recyclerView;
LinearLayout linearLayout;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
linearLayout = (LinearLayout) findViewById(R.id.container);
recyclerView = (RecyclerView) findViewById(R.id.my_recycler_view);
recyclerView.setHasFixedSize(true);
final LinearLayoutManager mgr = new LinearLayoutManager(this);
recyclerView.setLayoutManager(mgr);
recyclerView.setAdapter(new RecyclerAdapter());
recyclerView.addOnScrollListener(new MyScrollListener(mgr) {
#Override
public void scrollTheShit(int dy, int widthOfLastChild) {
linearLayout.setY(linearLayout.getY() + dy );
}
});
}
private class RecyclerAdapter extends Adapter<RecyclerAdapter.ViewHolder> {
#Override
public int getItemCount() {
return 10;
}
#Override
public void onBindViewHolder(ViewHolder viewHolder,
int position) {
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int position) {
View v = LayoutInflater.from(MainActivity.this)
.inflate(R.layout.child, parent, false);
ViewHolder holder = new ViewHolder(v);
return holder;
}
public class ViewHolder extends RecyclerView.ViewHolder {
public ViewHolder(View arg0) {
super(arg0);
}
}
}
}
I have to hide the top section when the below RecyclerView scrolls
how do I correct this ? anyone please help me
this may help you
QuickReturnRecyclerView.java
import android.content.Context;
import android.os.Build;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.TranslateAnimation;
import android.widget.FrameLayout;
public class QuickReturnRecyclerView extends RecyclerView {
private static final String TAG = QuickReturnRecyclerView.class.getName();
private View mReturningView;
private static final int STATE_ONSCREEN = 0;
private static final int STATE_OFFSCREEN = 1;
private static final int STATE_RETURNING = 2;
private int mState = STATE_ONSCREEN;
private int mMinRawY = 0;
private int mReturningViewHeight;
private int mGravity = Gravity.BOTTOM;
public QuickReturnRecyclerView(Context context) {
super(context);
init();
}
public QuickReturnRecyclerView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public QuickReturnRecyclerView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init(){
}
/**
* The view that should be showed/hidden when scrolling the content.
* Make sure to set the gravity on the this view to either Gravity.Bottom or
* Gravity.TOP and to put it preferable in a FrameLayout.
* #param view Any kind of view
*/
public void setReturningView(View view) {
mReturningView = view;
try {
FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) mReturningView.getLayoutParams();
mGravity = params.gravity;
} catch (ClassCastException e) {
throw new RuntimeException("The return view need to be put in a FrameLayout");
}
measureView(mReturningView);
mReturningViewHeight = mReturningView.getMeasuredHeight();
addOnScrollListener(new RecyclerScrollListener());
}
private void measureView(View child) {
ViewGroup.LayoutParams p = child.getLayoutParams();
if (p == null) {
p = new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.FILL_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
}
int childWidthSpec = ViewGroup.getChildMeasureSpec(0, 0, p.width);
int lpHeight = p.height;
int childHeightSpec;
if (lpHeight > 0) {
childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.EXACTLY);
} else {
childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
}
child.measure(childWidthSpec, childHeightSpec);
}
private class RecyclerScrollListener extends OnScrollListener {
private int mScrolledY;
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
if(mGravity == Gravity.BOTTOM)
mScrolledY += dy;
else if(mGravity == Gravity.TOP)
mScrolledY -= dy;
if(mReturningView == null)
return;
int translationY = 0;
int rawY = mScrolledY;
switch (mState) {
case STATE_OFFSCREEN:
if(mGravity == Gravity.BOTTOM) {
if (rawY >= mMinRawY) {
mMinRawY = rawY;
} else {
mState = STATE_RETURNING;
}
} else if(mGravity == Gravity.TOP) {
if (rawY <= mMinRawY) {
mMinRawY = rawY;
} else {
mState = STATE_RETURNING;
}
}
translationY = rawY;
break;
case STATE_ONSCREEN:
if(mGravity == Gravity.BOTTOM) {
if (rawY > mReturningViewHeight) {
mState = STATE_OFFSCREEN;
mMinRawY = rawY;
}
} else if(mGravity == Gravity.TOP) {
if (rawY < -mReturningViewHeight) {
mState = STATE_OFFSCREEN;
mMinRawY = rawY;
}
}
translationY = rawY;
break;
case STATE_RETURNING:
if(mGravity == Gravity.BOTTOM) {
translationY = (rawY - mMinRawY) + mReturningViewHeight;
if (translationY < 0) {
translationY = 0;
mMinRawY = rawY + mReturningViewHeight;
}
if (rawY == 0) {
mState = STATE_ONSCREEN;
translationY = 0;
}
if (translationY > mReturningViewHeight) {
mState = STATE_OFFSCREEN;
mMinRawY = rawY;
}
} else if(mGravity == Gravity.TOP) {
translationY = (rawY + Math.abs(mMinRawY)) - mReturningViewHeight;
if (translationY > 0) {
translationY = 0;
mMinRawY = rawY - mReturningViewHeight;
}
if (rawY == 0) {
mState = STATE_ONSCREEN;
translationY = 0;
}
if (translationY < -mReturningViewHeight) {
mState = STATE_OFFSCREEN;
mMinRawY = rawY;
}
}
break;
}
/** this can be used if the build is below honeycomb **/
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.HONEYCOMB) {
TranslateAnimation anim = new TranslateAnimation(0, 0, translationY, translationY);
anim.setFillAfter(true);
anim.setDuration(0);
mReturningView.startAnimation(anim);
} else {
mReturningView.setTranslationY(translationY);
}
}
}
}
activity_main.xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<yourpackagename.QuickReturnRecyclerView
android:id="#+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical" />
<LinearLayout
android:id="#+id/linear"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top"
android:background="#android:color/darker_gray"
android:orientation="horizontal"
android:padding="15dp">
<Button
android:id="#+id/button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="left|top"
android:layout_weight="1"
android:text="New Button" />
<Button
android:id="#+id/button2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal|top"
android:layout_weight="1"
android:text="New Button" />
<Button
android:id="#+id/button3"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="right|top"
android:layout_weight="1"
android:text="New Button" />
</LinearLayout>
</FrameLayout>
in MainActivity.java just add below lines
mRecyclerView = (QuickReturnRecyclerView) findViewById(R.id.recycler_view);
linear = (LinearLayout) findViewById(R.id.linear);
// use this setting to improve performance if you know that changes
// in content do not change the layout size of the RecyclerView
mRecyclerView.setHasFixedSize(true);
// use a linear layout manager
mLayoutManager = new LinearLayoutManager(getApplicationContext());
mRecyclerView.setLayoutManager(mLayoutManager);
mRecyclerView.setReturningView(linear);
I am using a floating action button in my activity. I have created a related layout with a black opacity background. I want it to appear when the floating action button is clicked. The relative layout will appear behind the action button to make it look prominent.
Here is my xml layout
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:fab="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.paaltao.activity.HomeActivity">
<include
android:id="#+id/app_bar"
layout="#layout/app_bar" />
<it.neokree.materialtabs.MaterialTabHost
android:id="#+id/materialTabHost"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_below="#+id/app_bar"
app:accentColor="#color/greenMaterial"
app:hasIcons="true"
app:primaryColor="#color/primaryColor"
app:textColor="#color/white" />
<android.support.v4.view.ViewPager
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#+id/materialTabHost" />
<RelativeLayout
android:id="#+id/white_opacity"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#+id/materialTabHost"
android:background="#color/black80"
android:visibility="gone" />
<com.paaltao.classes.FloatingActionsMenu
android:id="#+id/multiple_actions"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_gravity="end"
fab:fab_addButtonColorPressed="#color/white_pressed"
fab:fab_addButtonPlusIconColor="#color/white"
fab:fab_labelStyle="#style/menu_labels_style"
app:fab_addButtonColorNormal="#color/primaryColor">
<com.paaltao.classes.FloatingActionButton
android:id="#+id/action_b"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
fab:fab_colorNormal="#color/white"
fab:fab_colorPressed="#color/white_pressed"
fab:fab_title="Action B" />
</com.paaltao.classes.FloatingActionsMenu>
</RelativeLayout>
I am basically trying to set visibility of the relative layout to View.VISIBLE in the floating action menu class which extends a view group. Now I am getting null pointer exception when I am setting the visibility as View.VISIBLE in the button's onClick event.
Here is my Java Code :
package com.paaltao.classes;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.ColorRes;
import android.support.annotation.NonNull;
import android.util.AttributeSet;
import android.view.ContextThemeWrapper;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import android.view.animation.OvershootInterpolator;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.paaltao.R;
import java.util.zip.Inflater;
public class FloatingActionsMenu extends ViewGroup {
public static final int EXPAND_UP = 0;
public static final int EXPAND_DOWN = 1;
public static final int EXPAND_LEFT = 2;
public static final int EXPAND_RIGHT = 3;
private static final int ANIMATION_DURATION = 300;
private static final float COLLAPSED_PLUS_ROTATION = 0f;
private static final float EXPANDED_PLUS_ROTATION = 90f + 45f;
private int mAddButtonPlusColor;
private int mAddButtonColorNormal;
private int mAddButtonColorPressed;
private int mAddButtonSize;
private boolean mAddButtonStrokeVisible;
private int mExpandDirection;
private int mButtonSpacing;
private int mLabelsMargin;
private int mLabelsVerticalOffset;
private boolean mExpanded;
private AnimatorSet mExpandAnimation = new AnimatorSet().setDuration(ANIMATION_DURATION);
private AnimatorSet mCollapseAnimation = new AnimatorSet().setDuration(ANIMATION_DURATION);
private AddFloatingActionButton mAddButton;
private RotatingDrawable mRotatingDrawable;
private int mMaxButtonWidth;
private int mMaxButtonHeight;
private int mLabelsStyle;
private int mButtonsCount;
private OnFloatingActionsMenuUpdateListener mListener;
private RelativeLayout whiteOverlay;
public interface OnFloatingActionsMenuUpdateListener {
void onMenuExpanded();
void onMenuCollapsed();
}
public FloatingActionsMenu(Context context) {
this(context, null);
}
public FloatingActionsMenu(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public FloatingActionsMenu(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context, attrs);
}
private void init(Context context, AttributeSet attributeSet) {
mButtonSpacing = (int) (getResources().getDimension(R.dimen.fab_actions_spacing) - getResources().getDimension(R.dimen.fab_shadow_radius) - getResources().getDimension(R.dimen.fab_shadow_offset));
mLabelsMargin = getResources().getDimensionPixelSize(R.dimen.fab_labels_margin);
mLabelsVerticalOffset = getResources().getDimensionPixelSize(R.dimen.fab_shadow_offset);
TypedArray attr = context.obtainStyledAttributes(attributeSet, R.styleable.FloatingActionsMenu, 0, 0);
mAddButtonPlusColor = attr.getColor(R.styleable.FloatingActionsMenu_fab_addButtonPlusIconColor, getColor(android.R.color.white));
mAddButtonColorNormal = attr.getColor(R.styleable.FloatingActionsMenu_fab_addButtonColorNormal, getColor(android.R.color.holo_blue_dark));
mAddButtonColorPressed = attr.getColor(R.styleable.FloatingActionsMenu_fab_addButtonColorPressed, getColor(android.R.color.holo_blue_light));
mAddButtonSize = attr.getInt(R.styleable.FloatingActionsMenu_fab_addButtonSize, FloatingActionButton.SIZE_NORMAL);
mAddButtonStrokeVisible = attr.getBoolean(R.styleable.FloatingActionsMenu_fab_addButtonStrokeVisible, true);
mExpandDirection = attr.getInt(R.styleable.FloatingActionsMenu_fab_expandDirection, EXPAND_UP);
mLabelsStyle = attr.getResourceId(R.styleable.FloatingActionsMenu_fab_labelStyle, 0);
attr.recycle();
if (mLabelsStyle != 0 && expandsHorizontally()) {
throw new IllegalStateException("Action labels in horizontal expand orientation is not supported.");
}
createAddButton(context);
}
public void setOnFloatingActionsMenuUpdateListener(OnFloatingActionsMenuUpdateListener listener) {
mListener = listener;
}
private boolean expandsHorizontally() {
return mExpandDirection == EXPAND_LEFT || mExpandDirection == EXPAND_RIGHT;
}
private static class RotatingDrawable extends LayerDrawable {
public RotatingDrawable(Drawable drawable) {
super(new Drawable[] { drawable });
}
private float mRotation;
#SuppressWarnings("UnusedDeclaration")
public float getRotation() {
return mRotation;
}
#SuppressWarnings("UnusedDeclaration")
public void setRotation(float rotation) {
mRotation = rotation;
invalidateSelf();
}
#Override
public void draw(Canvas canvas) {
canvas.save();
canvas.rotate(mRotation, getBounds().centerX(), getBounds().centerY());
super.draw(canvas);
canvas.restore();
}
}
private void createAddButton(Context context) {
mAddButton = new AddFloatingActionButton(context) {
#Override
void updateBackground() {
mPlusColor = mAddButtonPlusColor;
mColorNormal = mAddButtonColorNormal;
mColorPressed = mAddButtonColorPressed;
mStrokeVisible = mAddButtonStrokeVisible;
super.updateBackground();
}
#Override
Drawable getIconDrawable() {
final RotatingDrawable rotatingDrawable = new RotatingDrawable(super.getIconDrawable());
mRotatingDrawable = rotatingDrawable;
final OvershootInterpolator interpolator = new OvershootInterpolator();
final ObjectAnimator collapseAnimator = ObjectAnimator.ofFloat(rotatingDrawable, "rotation", EXPANDED_PLUS_ROTATION, COLLAPSED_PLUS_ROTATION);
final ObjectAnimator expandAnimator = ObjectAnimator.ofFloat(rotatingDrawable, "rotation", COLLAPSED_PLUS_ROTATION, EXPANDED_PLUS_ROTATION);
collapseAnimator.setInterpolator(interpolator);
expandAnimator.setInterpolator(interpolator);
mExpandAnimation.play(expandAnimator);
mCollapseAnimation.play(collapseAnimator);
return rotatingDrawable;
}
};
mAddButton.setId(R.id.fab_expand_menu_button);
mAddButton.setSize(mAddButtonSize);
mAddButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
toggle();
// updateBackground();
}
});
addView(mAddButton, super.generateDefaultLayoutParams());
}
// public void updateBackground(){
// whiteOverlay = (RelativeLayout)findViewById(R.id.white_opacity);
// whiteOverlay.setVisibility(View.VISIBLE);
// }
public void addButton(FloatingActionButton button) {
addView(button, mButtonsCount - 1);
mButtonsCount++;
if (mLabelsStyle != 0) {
createLabels();
}
}
public void removeButton(FloatingActionButton button) {
removeView(button.getLabelView());
removeView(button);
mButtonsCount--;
}
private int getColor(#ColorRes int id) {
return getResources().getColor(id);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
measureChildren(widthMeasureSpec, heightMeasureSpec);
int width = 0;
int height = 0;
mMaxButtonWidth = 0;
mMaxButtonHeight = 0;
int maxLabelWidth = 0;
for (int i = 0; i < mButtonsCount; i++) {
View child = getChildAt(i);
if (child.getVisibility() == GONE) {
continue;
}
switch (mExpandDirection) {
case EXPAND_UP:
case EXPAND_DOWN:
mMaxButtonWidth = Math.max(mMaxButtonWidth, child.getMeasuredWidth());
height += child.getMeasuredHeight();
break;
case EXPAND_LEFT:
case EXPAND_RIGHT:
width += child.getMeasuredWidth();
mMaxButtonHeight = Math.max(mMaxButtonHeight, child.getMeasuredHeight());
break;
}
if (!expandsHorizontally()) {
TextView label = (TextView) child.getTag(R.id.fab_label);
if (label != null) {
maxLabelWidth = Math.max(maxLabelWidth, label.getMeasuredWidth());
}
}
}
if (!expandsHorizontally()) {
width = mMaxButtonWidth + (maxLabelWidth > 0 ? maxLabelWidth + mLabelsMargin : 0);
} else {
height = mMaxButtonHeight;
}
switch (mExpandDirection) {
case EXPAND_UP:
case EXPAND_DOWN:
height += mButtonSpacing * (getChildCount() - 1);
height = adjustForOvershoot(height);
break;
case EXPAND_LEFT:
case EXPAND_RIGHT:
width += mButtonSpacing * (getChildCount() - 1);
width = adjustForOvershoot(width);
break;
}
setMeasuredDimension(width, height);
}
private int adjustForOvershoot(int dimension) {
return dimension * 12 / 10;
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
switch (mExpandDirection) {
case EXPAND_UP:
case EXPAND_DOWN:
boolean expandUp = mExpandDirection == EXPAND_UP;
int addButtonY = expandUp ? b - t - mAddButton.getMeasuredHeight() : 0;
// Ensure mAddButton is centered on the line where the buttons should be
int addButtonLeft = r - l - mMaxButtonWidth + (mMaxButtonWidth - mAddButton.getMeasuredWidth()) / 2;
mAddButton.layout(addButtonLeft, addButtonY, addButtonLeft + mAddButton.getMeasuredWidth(), addButtonY + mAddButton.getMeasuredHeight());
int labelsRight = r - l - mMaxButtonWidth - mLabelsMargin;
int nextY = expandUp ?
addButtonY - mButtonSpacing :
addButtonY + mAddButton.getMeasuredHeight() + mButtonSpacing;
for (int i = mButtonsCount - 1; i >= 0; i--) {
final View child = getChildAt(i);
if (child == mAddButton || child.getVisibility() == GONE) continue;
int childX = addButtonLeft + (mAddButton.getMeasuredWidth() - child.getMeasuredWidth()) / 2;
int childY = expandUp ? nextY - child.getMeasuredHeight() : nextY;
child.layout(childX, childY, childX + child.getMeasuredWidth(), childY + child.getMeasuredHeight());
float collapsedTranslation = addButtonY - childY;
float expandedTranslation = 0f;
child.setTranslationY(mExpanded ? expandedTranslation : collapsedTranslation);
child.setAlpha(mExpanded ? 1f : 0f);
LayoutParams params = (LayoutParams) child.getLayoutParams();
params.mCollapseDir.setFloatValues(expandedTranslation, collapsedTranslation);
params.mExpandDir.setFloatValues(collapsedTranslation, expandedTranslation);
params.setAnimationsTarget(child);
View label = (View) child.getTag(R.id.fab_label);
if (label != null) {
int labelLeft = labelsRight - label.getMeasuredWidth();
int labelTop = childY - mLabelsVerticalOffset + (child.getMeasuredHeight() - label.getMeasuredHeight()) / 2;
label.layout(labelLeft, labelTop, labelsRight, labelTop + label.getMeasuredHeight());
label.setTranslationY(mExpanded ? expandedTranslation : collapsedTranslation);
label.setAlpha(mExpanded ? 1f : 0f);
LayoutParams labelParams = (LayoutParams) label.getLayoutParams();
labelParams.mCollapseDir.setFloatValues(expandedTranslation, collapsedTranslation);
labelParams.mExpandDir.setFloatValues(collapsedTranslation, expandedTranslation);
labelParams.setAnimationsTarget(label);
}
nextY = expandUp ?
childY - mButtonSpacing :
childY + child.getMeasuredHeight() + mButtonSpacing;
}
break;
case EXPAND_LEFT:
case EXPAND_RIGHT:
boolean expandLeft = mExpandDirection == EXPAND_LEFT;
int addButtonX = expandLeft ? r - l - mAddButton.getMeasuredWidth() : 0;
// Ensure mAddButton is centered on the line where the buttons should be
int addButtonTop = b - t - mMaxButtonHeight + (mMaxButtonHeight - mAddButton.getMeasuredHeight()) / 2;
mAddButton.layout(addButtonX, addButtonTop, addButtonX + mAddButton.getMeasuredWidth(), addButtonTop + mAddButton.getMeasuredHeight());
int nextX = expandLeft ?
addButtonX - mButtonSpacing :
addButtonX + mAddButton.getMeasuredWidth() + mButtonSpacing;
for (int i = mButtonsCount - 1; i >= 0; i--) {
final View child = getChildAt(i);
if (child == mAddButton || child.getVisibility() == GONE) continue;
int childX = expandLeft ? nextX - child.getMeasuredWidth() : nextX;
int childY = addButtonTop + (mAddButton.getMeasuredHeight() - child.getMeasuredHeight()) / 2;
child.layout(childX, childY, childX + child.getMeasuredWidth(), childY + child.getMeasuredHeight());
float collapsedTranslation = addButtonX - childX;
float expandedTranslation = 0f;
child.setTranslationX(mExpanded ? expandedTranslation : collapsedTranslation);
child.setAlpha(mExpanded ? 1f : 0f);
LayoutParams params = (LayoutParams) child.getLayoutParams();
params.mCollapseDir.setFloatValues(expandedTranslation, collapsedTranslation);
params.mExpandDir.setFloatValues(collapsedTranslation, expandedTranslation);
params.setAnimationsTarget(child);
nextX = expandLeft ?
childX - mButtonSpacing :
childX + child.getMeasuredWidth() + mButtonSpacing;
}
break;
}
}
#Override
protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
return new LayoutParams(super.generateDefaultLayoutParams());
}
#Override
public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
return new LayoutParams(super.generateLayoutParams(attrs));
}
#Override
protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
return new LayoutParams(super.generateLayoutParams(p));
}
#Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
return super.checkLayoutParams(p);
}
private static Interpolator sExpandInterpolator = new OvershootInterpolator();
private static Interpolator sCollapseInterpolator = new DecelerateInterpolator(3f);
private static Interpolator sAlphaExpandInterpolator = new DecelerateInterpolator();
private class LayoutParams extends ViewGroup.LayoutParams {
private ObjectAnimator mExpandDir = new ObjectAnimator();
private ObjectAnimator mExpandAlpha = new ObjectAnimator();
private ObjectAnimator mCollapseDir = new ObjectAnimator();
private ObjectAnimator mCollapseAlpha = new ObjectAnimator();
private boolean animationsSetToPlay;
public LayoutParams(ViewGroup.LayoutParams source) {
super(source);
mExpandDir.setInterpolator(sExpandInterpolator);
mExpandAlpha.setInterpolator(sAlphaExpandInterpolator);
mCollapseDir.setInterpolator(sCollapseInterpolator);
mCollapseAlpha.setInterpolator(sCollapseInterpolator);
mCollapseAlpha.setProperty(View.ALPHA);
mCollapseAlpha.setFloatValues(1f, 0f);
mExpandAlpha.setProperty(View.ALPHA);
mExpandAlpha.setFloatValues(0f, 1f);
switch (mExpandDirection) {
case EXPAND_UP:
case EXPAND_DOWN:
mCollapseDir.setProperty(View.TRANSLATION_Y);
mExpandDir.setProperty(View.TRANSLATION_Y);
break;
case EXPAND_LEFT:
case EXPAND_RIGHT:
mCollapseDir.setProperty(View.TRANSLATION_X);
mExpandDir.setProperty(View.TRANSLATION_X);
break;
}
}
public void setAnimationsTarget(View view) {
mCollapseAlpha.setTarget(view);
mCollapseDir.setTarget(view);
mExpandAlpha.setTarget(view);
mExpandDir.setTarget(view);
// Now that the animations have targets, set them to be played
if (!animationsSetToPlay) {
mCollapseAnimation.play(mCollapseAlpha);
mCollapseAnimation.play(mCollapseDir);
mExpandAnimation.play(mExpandAlpha);
mExpandAnimation.play(mExpandDir);
animationsSetToPlay = true;
}
}
}
#Override
protected void onFinishInflate() {
super.onFinishInflate();
bringChildToFront(mAddButton);
mButtonsCount = getChildCount();
if (mLabelsStyle != 0) {
createLabels();
}
}
private void createLabels() {
Context context = new ContextThemeWrapper(getContext(), mLabelsStyle);
for (int i = 0; i < mButtonsCount; i++) {
FloatingActionButton button = (FloatingActionButton) getChildAt(i);
String title = button.getTitle();
if (button == mAddButton || title == null ||
button.getTag(R.id.fab_label) != null) continue;
TextView label = new TextView(context);
label.setText(button.getTitle());
addView(label);
button.setTag(R.id.fab_label, label);
}
}
public void collapse() {
if (mExpanded) {
mExpanded = false;
mCollapseAnimation.start();
mExpandAnimation.cancel();
if (mListener != null) {
mListener.onMenuCollapsed();
}
}
}
public void toggle() {
if (mExpanded) {
collapse();
} else {
expand();
}
}
public void expand() {
if (!mExpanded) {
mExpanded = true;
mCollapseAnimation.cancel();
mExpandAnimation.start();
if (mListener != null) {
mListener.onMenuExpanded();
}
}
}
public boolean isExpanded() {
return mExpanded;
}
#Override
public Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
SavedState savedState = new SavedState(superState);
savedState.mExpanded = mExpanded;
return savedState;
}
#Override
public void onRestoreInstanceState(Parcelable state) {
if (state instanceof SavedState) {
SavedState savedState = (SavedState) state;
mExpanded = savedState.mExpanded;
if (mRotatingDrawable != null) {
mRotatingDrawable.setRotation(mExpanded ? EXPANDED_PLUS_ROTATION : COLLAPSED_PLUS_ROTATION);
}
super.onRestoreInstanceState(savedState.getSuperState());
} else {
super.onRestoreInstanceState(state);
}
}
public static class SavedState extends BaseSavedState {
public boolean mExpanded;
public SavedState(Parcelable parcel) {
super(parcel);
}
private SavedState(Parcel in) {
super(in);
mExpanded = in.readInt() == 1;
}
#Override
public void writeToParcel(#NonNull Parcel out, int flags) {
super.writeToParcel(out, flags);
out.writeInt(mExpanded ? 1 : 0);
}
public static final Creator<SavedState> CREATOR = new Creator<SavedState>() {
#Override
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
#Override
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
}
}
Please Help.
If I understand correctly, the code causing the problem is the one commented, otherwise this line :
whiteOverlay.setVisibility(View.VISIBLE);
You are trying to access your RelativeLayout (R.id.white_opacity) through your FloatingActionsMenu view :
whiteOverlay = (RelativeLayout)findViewById(R.id.white_opacity);
But R.id.white_opacity is a member of your HomeActivity view.
So it's normal that this line returns a null pointer since it doesn't have any member named like this.
Try do find a way to access your HomeActivity view and then call "findViewById()" on it.
If "context" is indeed this HomeActivity, then pass the argument to the updateBackground() method (or set context as a class member) and try somthing like that :
whiteOverlay = ((Activity)context).findViewById(R.id.white_opacity);
Regards.
Here is my xml:
Actually i making app of photo editing, when i click edit menu pop up list in bottom with different image filter click and apply. that i want to do any help or reference will appreciated
ListViewDemo.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_gravity="bottom"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#fff"
>
<com.example.horizontallistview_snapit.HorizontalListView
android:id="#+id/listview"
android:layout_width="fill_parent"
android:layout_gravity="bottom"
android:layout_height="wrap_content"
android:background="#ddd"
/>
</LinearLayout>
main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="bottom"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="#string/hello_world"
/>
viewitem.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#fff"
>
<ImageView
android:id="#+id/image"
android:layout_width="150dip"
android:layout_height="150dip"
android:scaleType="centerCrop"
android:src="#drawable/ic_launcher"
/>
<TextView
android:id="#+id/title"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textColor="#000"
android:gravity="center_horizontal"
/>
<Button
android:id="#+id/clickbutton"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Click Me"
android:textColor="#000" />
</LinearLayout>
HorizontalListView.java
import java.util.LinkedList;
import java.util.Queue;
import android.content.Context;
import android.database.DataSetObserver;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.GestureDetector.OnGestureListener;
import android.view.MotionEvent;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListAdapter;
import android.widget.Scroller;
public class HorizontalListView extends AdapterView<ListAdapter> {
public boolean mAlwaysOverrideTouch = true;
protected ListAdapter mAdapter;
private int mLeftViewIndex = -1;
private int mRightViewIndex = 0;
protected int mCurrentX;
protected int mNextX;
private int mMaxX = Integer.MAX_VALUE;
private int mDisplayOffset = 0;
protected Scroller mScroller;
private GestureDetector mGesture;
private Queue<View> mRemovedViewQueue = new LinkedList<View>();
private OnItemSelectedListener mOnItemSelected;
private OnItemClickListener mOnItemClicked;
private OnItemLongClickListener mOnItemLongClicked;
private boolean mDataChanged = false;
public HorizontalListView(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
}
private synchronized void initView() {
mLeftViewIndex = -1;
mRightViewIndex = 0;
mDisplayOffset = 0;
mCurrentX = 0;
mNextX = 0;
mMaxX = Integer.MAX_VALUE;
mScroller = new Scroller(getContext());
mGesture = new GestureDetector(getContext(), mOnGesture);
}
#Override
public void setOnItemSelectedListener(AdapterView.OnItemSelectedListener listener) {
mOnItemSelected = listener;
}
#Override
public void setOnItemClickListener(AdapterView.OnItemClickListener listener){
mOnItemClicked = listener;
}
#Override
public void setOnItemLongClickListener(AdapterView.OnItemLongClickListener listener) {
mOnItemLongClicked = listener;
}
private DataSetObserver mDataObserver = new DataSetObserver() {
#Override
public void onChanged() {
synchronized(HorizontalListView.this){
mDataChanged = true;
}
invalidate();
requestLayout();
}
#Override
public void onInvalidated() {
reset();
invalidate();
requestLayout();
}
};
#Override
public ListAdapter getAdapter() {
return mAdapter;
}
#Override
public View getSelectedView() {
//TODO: implement
return null;
}
#Override
public void setAdapter(ListAdapter adapter) {
if(mAdapter != null) {
mAdapter.unregisterDataSetObserver(mDataObserver);
}
mAdapter = adapter;
mAdapter.registerDataSetObserver(mDataObserver);
reset();
}
private synchronized void reset(){
initView();
removeAllViewsInLayout();
requestLayout();
}
#Override
public void setSelection(int position) {
//TODO: implement
}
private void addAndMeasureChild(final View child, int viewPos) {
LayoutParams params = child.getLayoutParams();
if(params == null) {
params = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
}
addViewInLayout(child, viewPos, params, true);
child.measure(MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.AT_MOST),
MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.AT_MOST));
}
#Override
protected synchronized void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
if(mAdapter == null){
return;
}
if(mDataChanged){
int oldCurrentX = mCurrentX;
initView();
removeAllViewsInLayout();
mNextX = oldCurrentX;
mDataChanged = false;
}
if(mScroller.computeScrollOffset()){
int scrollx = mScroller.getCurrX();
mNextX = scrollx;
}
if(mNextX <= 0){
mNextX = 0;
mScroller.forceFinished(true);
}
if(mNextX >= mMaxX) {
mNextX = mMaxX;
mScroller.forceFinished(true);
}
int dx = mCurrentX - mNextX;
removeNonVisibleItems(dx);
fillList(dx);
positionItems(dx);
mCurrentX = mNextX;
if(!mScroller.isFinished()){
post(new Runnable(){
#Override
public void run() {
requestLayout();
}
});
}
}
private void fillList(final int dx) {
int edge = 0;
View child = getChildAt(getChildCount()-1);
if(child != null) {
edge = child.getRight();
}
fillListRight(edge, dx);
edge = 0;
child = getChildAt(0);
if(child != null) {
edge = child.getLeft();
}
fillListLeft(edge, dx);
}
private void fillListRight(int rightEdge, final int dx) {
while(rightEdge + dx < getWidth() && mRightViewIndex < mAdapter.getCount()) {
View child = mAdapter.getView(mRightViewIndex, mRemovedViewQueue.poll(), this);
addAndMeasureChild(child, -1);
rightEdge += child.getMeasuredWidth();
if(mRightViewIndex == mAdapter.getCount()-1) {
mMaxX = mCurrentX + rightEdge - getWidth();
}
if (mMaxX < 0) {
mMaxX = 0;
}
mRightViewIndex++;
}
}
private void fillListLeft(int leftEdge, final int dx) {
while(leftEdge + dx > 0 && mLeftViewIndex >= 0) {
View child = mAdapter.getView(mLeftViewIndex, mRemovedViewQueue.poll(), this);
addAndMeasureChild(child, 0);
leftEdge -= child.getMeasuredWidth();
mLeftViewIndex--;
mDisplayOffset -= child.getMeasuredWidth();
}
}
private void removeNonVisibleItems(final int dx) {
View child = getChildAt(0);
while(child != null && child.getRight() + dx <= 0) {
mDisplayOffset += child.getMeasuredWidth();
mRemovedViewQueue.offer(child);
removeViewInLayout(child);
mLeftViewIndex++;
child = getChildAt(0);
}
child = getChildAt(getChildCount()-1);
while(child != null && child.getLeft() + dx >= getWidth()) {
mRemovedViewQueue.offer(child);
removeViewInLayout(child);
mRightViewIndex--;
child = getChildAt(getChildCount()-1);
}
}
private void positionItems(final int dx) {
if(getChildCount() > 0){
mDisplayOffset += dx;
int left = mDisplayOffset;
for(int i=0;i<getChildCount();i++){
View child = getChildAt(i);
int childWidth = child.getMeasuredWidth();
child.layout(left, 0, left + childWidth, child.getMeasuredHeight());
left += childWidth + child.getPaddingRight();
}
}
}
public synchronized void scrollTo(int x) {
mScroller.startScroll(mNextX, 0, x - mNextX, 0);
requestLayout();
}
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
boolean handled = super.dispatchTouchEvent(ev);
handled |= mGesture.onTouchEvent(ev);
return handled;
}
protected boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
synchronized(HorizontalListView.this){
mScroller.fling(mNextX, 0, (int)-velocityX, 0, 0, mMaxX, 0, 0);
}
requestLayout();
return true;
}
protected boolean onDown(MotionEvent e) {
mScroller.forceFinished(true);
return true;
}
private OnGestureListener mOnGesture = new GestureDetector.SimpleOnGestureListener() {
#Override
public boolean onDown(MotionEvent e) {
return HorizontalListView.this.onDown(e);
}
#Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
return HorizontalListView.this.onFling(e1, e2, velocityX, velocityY);
}
#Override
public boolean onScroll(MotionEvent e1, MotionEvent e2,
float distanceX, float distanceY) {
synchronized(HorizontalListView.this){
mNextX += (int)distanceX;
}
requestLayout();
return true;
}
#Override
public boolean onSingleTapConfirmed(MotionEvent e) {
for(int i=0;i<getChildCount();i++){
View child = getChildAt(i);
if (isEventWithinView(e, child)) {
if(mOnItemClicked != null){
mOnItemClicked.onItemClick(HorizontalListView.this, child, mLeftViewIndex + 1 + i, mAdapter.getItemId( mLeftViewIndex + 1 + i ));
}
if(mOnItemSelected != null){
mOnItemSelected.onItemSelected(HorizontalListView.this, child, mLeftViewIndex + 1 + i, mAdapter.getItemId( mLeftViewIndex + 1 + i ));
}
break;
}
}
return true;
}
#Override
public void onLongPress(MotionEvent e) {
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
if (isEventWithinView(e, child)) {
if (mOnItemLongClicked != null) {
mOnItemLongClicked.onItemLongClick(HorizontalListView.this, child, mLeftViewIndex + 1 + i, mAdapter.getItemId(mLeftViewIndex + 1 + i));
}
break;
}
}
}
private boolean isEventWithinView(MotionEvent e, View child) {
Rect viewRect = new Rect();
int[] childPosition = new int[2];
child.getLocationOnScreen(childPosition);
int left = childPosition[0];
int right = left + child.getWidth();
int top = childPosition[1];
int bottom = top + child.getHeight();
viewRect.set(left, top, right, bottom);
return viewRect.contains((int) e.getRawX(), (int) e.getRawY());
}
};
}
**HorizontalListViewDemo.java**
import android.app.Activity;
import android.app.AlertDialog;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.TextView;
public class HorizontalListViewDemo extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.listviewdemo);
HorizontalListView listview = (HorizontalListView) findViewById(R.id.listview);
listview.setAdapter(mAdapter);
}
private static String[] dataObjects = new String[]{ "Text #1",
"Text #2",
"Text #3","Text #4","Text #5","Text #6","Text #7","Text #8","Text #9","Text #10" };
private BaseAdapter mAdapter = new BaseAdapter() {
private OnClickListener mOnButtonClicked = new OnClickListener() {
#Override
public void onClick(View v) {
AlertDialog.Builder builder = new AlertDialog.Builder(HorizontalListViewDemo.this);
builder.setMessage("hello from " + v);
builder.setPositiveButton("Cool", null);
builder.show();
}
};
#Override
public int getCount() {
return dataObjects.length;
}
#Override
public Object getItem(int position) {
return null;
}
#Override
public long getItemId(int position) {
return 0;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View retval = LayoutInflater.from(parent.getContext()).inflate(R.layout.viewitem, null);
TextView title = (TextView) retval.findViewById(R.id.title);
Button button = (Button) retval.findViewById(R.id.clickbutton);
button.setOnClickListener(mOnButtonClicked);
title.setText(dataObjects[position]);
return retval;
}
};
}
My Above code is working fine, it showing me horizontal list view but in top, my question is that, how to make list in bottom, i myself tried set gravity bottom but not working fine. Any help please? Thanks
Try this..
Set android:gravity="bottom" to main LinearLayout and give HorizontalListView height as 100dp like android:layout_height="100dp".
Without given height as specific you cannot fix the HorizontalListView at the bottom
<?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:background="#fff"
android:gravity="bottom"
android:orientation="vertical" >
<com.example.horizontallistview_snapit.HorizontalListView
android:id="#+id/listview"
android:layout_width="fill_parent"
android:layout_height="100dp"
android:layout_gravity="bottom"
android:background="#ddd" />
</LinearLayout>
Instead of linearLayout use RelativeLayout.
Or set android:gravity="bottom" to linearLayout
Like vipul mittal said, use a RelativeLayout instead, but you don't have to change very much.
Wrap your current LinearLayout in a RelativeLayout and add to the LinearLayout the property
android:alignParentBottom="true" and make the RelativeLayout's height fill_parent.
I'd just get rid of your LinearLayout and replace it with a RelativeLayout and use the same property that I mentioned previously to align the view to parent's bottom.
I am providing you some code how to use relative Layout.Hope this help you:-
<RelativeLayout
android:id="#+id/panel1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#ffffff">
<Button
android:id="#+id/btnGalleryFullScreenClose"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_alignParentLeft="true"
android:background="#drawable/close"
android:layout_marginLeft="5dp"
android:gravity="top" />
<Button
android:id="#+id/btnGalleryFullScreenForward"
android:layout_width="90dp"
android:layout_height="40dp"
android:layout_alignParentRight="true"
android:layout_marginRight="5dp"
android:background="#drawable/share" />
<TextView
android:id="#+id/txtGalleryFullScreenTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="#+id/btnGalleryFullScreenClose"
android:gravity="center_horizontal"
android:layout_toLeftOf="#+id/btnGalleryFullScreenForward"
android:text="Hello World"
android:textColor="#555555"
android:textSize="24sp" />
</RelativeLayout>