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.
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 want to develop my own Accept and Decline buttons for an incoming call. To prevent the call to be accidentally answered or rejected when taking the phone out of the pocket I would like to make a slider style button or something similar. I am, to accept the call is not just to tap on the Accept button. It would be more like sliding the finger from left to right (or opposite) and let the button get wider with the moment. Just like Android does.
Is there any way to make this? Any hint?
I hope to be clear.
How about create an image and slide it to the right (or left) and then send the event to an Activity or any view that you wanna handle the result?
For this, you can created a custom view which implements OnTouchListener :
public class ImageTouchSlider extends RelativeLayout implements View.OnTouchListener {
private Context mContext;
private ImageView mImage;
private int mScreenWidthInPixel;
private int mScreenWidthInDp;
private float mDensity;
private int mPaddingInDp = 15;
private int mPaddingInPixel;
private int mLengthOfSlider;
public interface OnImageSliderChangedListener{
void onChanged();
}
private OnImageSliderChangedListener mOnImageSliderChangedListener;
public ImageTouchSlider(Context context) {
super(context);
mContext = context;
createView();
}
public ImageTouchSlider(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
createView();
}
public ImageTouchSlider(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mContext = context;
createView();
}
public void createView() {
LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.image_touch_slider, this, true);
mImage = (ImageView) findViewById(R.id.slider_image);
mImage.setOnTouchListener(this);
WindowManager manager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
Display display = manager.getDefaultDisplay();
DisplayMetrics outMetrics = new DisplayMetrics ();
display.getMetrics(outMetrics);
mDensity = getResources().getDisplayMetrics().density;
float dpWidth = outMetrics.widthPixels / mDensity;
mScreenWidthInPixel = outMetrics.widthPixels;
mScreenWidthInDp = (int) (mScreenWidthInPixel / mDensity);
mLengthOfSlider = (int) (mScreenWidthInDp - mPaddingInDp*2);
}
#Override
public boolean onTouch(View v, MotionEvent event) {
LayoutParams layoutParams = (LayoutParams) v.getLayoutParams();
int width = v.getWidth();
float xPos = event.getRawX();
switch(event.getAction()) {
case MotionEvent.ACTION_DOWN:
// You can add some clicked reaction here.
break;
case MotionEvent.ACTION_MOVE:
if(xPos < (mScreenWidthInPixel - width - mPaddingInDp*mDensity) && xPos > mPaddingInDp*mDensity) {
mOnImageSliderChangedListener.onChanged();
layoutParams.leftMargin = (int) xPos - width / 2;
mImage.setLayoutParams(layoutParams);
}
break;
case MotionEvent.ACTION_UP:
break;
default:
break;
}
return true;
}
public void setOnImageSliderChangedListener(OnImageSliderChangedListener listener) {
mOnImageSliderChangedListener = listener;
}
} //end of class
image_touch_slider.xml layout :
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<ImageView
android:id="#+id/slider"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_centerVertical="true"
android:src="#drawable/your_drawable" />
</RelativeLayout>
You can modify screen width calculation part (my current code is not so clean), and add this view in .xml like this :
<com.your.package.path.ImageTouchSlider
android:id="#+id/slider"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
In your class, you can find this view :
ImageTouchSlider slider = (ImageTouchSlider) findViewById(R.id.slider);
slider.setOnImageSliderChangedListener(new ImageTouchSlider.OnImageSliderChangedListener() {
#Override
public void onChanged() {
// do something what you want here.
}
});
Hope this can help! :)
If you have your own sliding layout then see this code, might be helpful for you.
public class UnlockBar extends RelativeLayout
{
private OnUnlockListener listener = null;
private TextView text_label = null;
private ImageView img_thumb = null;
private int thumbWidth = 0;
boolean sliding = false;
private int sliderPosition = 0;
int initialSliderPosition = 0;
float initialSlidingX = 0;
public UnlockBar(Context context)
{
super(context);
init(context, null);
}
public UnlockBar(Context context, AttributeSet attrs)
{
super(context, attrs);
init(context, attrs);
}
public UnlockBar(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
init(context, attrs);
}
public void setOnUnlockListener(OnUnlockListener listener)
{
this.listener = listener;
}
public void reset()
{
final RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) img_thumb.getLayoutParams();
ValueAnimator animator = ValueAnimator.ofInt(params.leftMargin, 0);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator)
{
params.leftMargin = (Integer) valueAnimator.getAnimatedValue();
img_thumb.requestLayout();
}
});
animator.setDuration(300);
animator.start();
text_label.setAlpha(1f);
}
private void init(Context context, AttributeSet attrs)
{
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.unlock_main, this, true);
// Retrieve layout elements
text_label = (TextView) findViewById(R.id.text_label);
img_thumb = (ImageView) findViewById(R.id.img_thumb);
// Get padding
thumbWidth = dpToPx(80); // 60dp + 2*10dp
}
#Override
#SuppressLint("ClickableViewAccessibility")
public boolean onTouchEvent(MotionEvent event)
{
super.onTouchEvent(event);
if (event.getAction() == MotionEvent.ACTION_DOWN)
{
if (event.getX() > sliderPosition && event.getX() < (sliderPosition + thumbWidth))
{
sliding = true;
initialSlidingX = event.getX();
initialSliderPosition = sliderPosition;
}
}
else if (event.getAction() == MotionEvent.ACTION_UP || event.getAction() == MotionEvent.ACTION_OUTSIDE)
{
if (sliderPosition >= (getMeasuredWidth() - thumbWidth))
{
if (listener != null) listener.onUnlock();
}
else
{
sliding = false;
sliderPosition = 0;
reset();
}
}
else if (event.getAction() == MotionEvent.ACTION_MOVE && sliding)
{
sliderPosition = (int) (initialSliderPosition + (event.getX() - initialSlidingX));
if (sliderPosition <= 0) sliderPosition = 0;
if (sliderPosition >= (getMeasuredWidth() - thumbWidth))
{
sliderPosition = (int) (getMeasuredWidth() - thumbWidth);
}
else
{
int max = (int) (getMeasuredWidth() - thumbWidth);
int progress = (int) (sliderPosition * 100 / (max * 1.0f));
text_label.setAlpha(1f - progress * 0.02f);
}
setMarginLeft(sliderPosition);
}
return true;
}
private void setMarginLeft(int margin)
{
if (img_thumb == null) return;
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) img_thumb.getLayoutParams();
params.setMargins(margin, 0, 0, 0);
img_thumb.setLayoutParams(params);
}
private int dpToPx(int dp)
{
float density = getResources().getDisplayMetrics().density;
return Math.round((float)dp * density);
}
public static interface OnUnlockListener {
void onUnlock();
}
}
And just set the listener in main activity
UnlockBar unlock = (UnlockBar) findViewById(R.id.unlock);
// Attach listener
unlock.setOnUnlockListener(new OnUnlockListener() {
#Override
public void onUnlock()
{
Toast.makeText(TestActivity.this, "You've successfully unlocked it !", Toast.LENGTH_LONG).show();
}
});
And draw your own slide_image_layout.xml
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="80dp"
android:layout_width="match_parent"
android:background="#000000"
android:padding="10dp">
<ImageView
android:layout_height="match_parent"
android:layout_width="wrap_content"
android:layout_alignParentLeft="true"
android:src="#drawable/unlock_left"
android:contentDescription="#string/unlock_locked" />
<ImageView
android:layout_height="match_parent"
android:layout_width="wrap_content"
android:layout_alignParentRight="true"
android:src="#drawable/unlock_right"
android:contentDescription="#string/unlock_unlocked" />
<TextView
android:id="#+id/text_label"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:layout_centerInParent="true"
android:gravity="center"
android:text="#string/unlock_instructions"
android:textColor="#android:color/white"
android:textSize="18sp"
android:textStyle="italic" />
<ImageView
android:id="#+id/img_thumb"
android:layout_height="match_parent"
android:layout_width="wrap_content"
android:src="#drawable/unlock_thumb"
android:contentDescription="#string/unlock_thumb" />
</RelativeLayout>
And in your main_layout.xml add this ..
<com.hamondigital.unlock.UnlockBar
android:id="#+id/unlock"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
In my application within a scroll view, I am using list view. But the list view is not scrolling. Can anyone suggest me what should i do.
I searched for it and find out that the list view don't scroll within scroll view.
Any solution?
You can create your own list view and set expand to false. Here is the sample class
public class ExpandableHeightListView extends ListView {
boolean expanded = false;
public ExpandableHeightListView(Context context) {
super(context);
}
public ExpandableHeightListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ExpandableHeightListView(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
}
public boolean isExpanded() {
return expanded;
}
#Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// HACK! TAKE THAT ANDROID!
if (isExpanded()) {
int expandSpec = MeasureSpec.makeMeasureSpec(
Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, expandSpec);
ViewGroup.LayoutParams params = getLayoutParams();
params.height = getMeasuredHeight();
} else {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
public void setExpanded(boolean expanded) {
this.expanded = expanded;
}
}
You can use like this your activity.clas
ExpandableHeightListView listview = (ExpandableHeightListView) findViewById(R.id.list);
listview.setExpanded(true);
In your layout file you can use ExpandableHeightListView at the place of list view within a scroll view. It will scroll.
Exclude ListView from ScrollView, because ListView already have scrolling mechanism in it.
Don't put a listview inside a scrollview.
The listview already handles scrolling so it doesn't need to be inside a scrollview.
You should change your layouts to reflect this.
ListView Inside Scroll View will not work . Put it out side of that . Because both have scrolling feature so scroll will not work when thay will come together .
Use this Class.
public class CustomScrollView extends ScrollView {
public CustomScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public boolean onTouchEvent(MotionEvent ev) {
return true;
}
public boolean onInterceptTouchEvent(MotionEvent ev) {
return false;
}
}
And in your xml layout change your scrollview tag with the package name and class of the CustomScrollView. i.e. change to com.test.CustomScrollView.
And inside you Activity get the id of the custom scroll view and include this code.
private int currentX, currentY;
private CustomScrollView customScrollView;
customScrollView.setSmoothScrollingEnabled(true);
customScrollView.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
switch(event.getAction()){
case MotionEvent.ACTION_DOWN: {
currentX = (int) event.getRawX();
currentY = (int) event.getRawY();
break;
}
case MotionEvent.ACTION_MOVE: {
#SuppressWarnings("unused")
int x2 = (int) event.getRawX();
int y2 = (int) event.getRawY();
customScrollView.scrollBy(0 , currentY - y2);
currentY = y2;
break;
}
case MotionEvent.ACTION_UP: {
break;
}
}
return true;
}
});
Don't put a listView inside a ScrollView.
You should read the Romain Guy answer and the comment above:
How can I put a ListView into a ScrollView without it collapsing?
You can exclude ListView from Scroll view.
If you would like to have a "list" inside a scrollView you could use a LinearLayout. Something like this:
public class MyListLayout extends LinearLayout implements
View.OnClickListener {
private Adapter list;
private View.OnClickListener mListener;
public MyListLayout(Context context) {
super(context);
}
public MyListLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public EvernoteListLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
public void onClick(View v) {
if (mListener!=null)
mListener.onClick(v);
}
public void setList(Adapter list) {
this.list = list;
//Popolute list
if (this.list!=null){
for (int i=0;i<this.list.getCount();i++){
View item= list.getView(i, null,null);
this.addView(item);
}
}
}
public void setmListener(View.OnClickListener mListener) {
this.mListener = mListener;
}
}
// Create ArrayAdapter
MyListAdapter mListAdapter = new MyListAdapter();
MyListLayout mLay = (MyListLayout) findViewById(R.id.box_list_ev);
if (mLay != null) {
mLay.setList(mListAdapter);
}
The scroll view "eats" all the touches...
You should avoid inserting a scrolling element (ListView) into another scrolling element(scroll view), your user could go crazy.
In these cases I use a LinearLayout in place of the ListView and I inflate into it the customview created to represent each List element, here is an example, fell free to ask me more:
LinearLayout containerLinearLayout= (LinearLayout)findViewById(R.id.containerLinearLayout);
containerLinearLayout.removeAllViews();
for(int i=0;i<array.length();i++){
JSONObject convocazione=array.getJSONObject(i);
View child = getLayoutInflater().inflate(R.layout.list_element_layout,null);
TextView txtDettaglioLuogo=(TextView)child.findViewById(R.id.txtDettaglioLuogo);
TextView txtOra=(TextView)child.findViewById(R.id.txtOra);
txtOra.setText("text");
txtData.setText("more text");
containerLinearLayout.addView(child);
}
import android.content.Context;
import android.widget.ListView;
public class MyListView extends ListView {
public MyListView(Context context) {
super(context);
}
public MyListView(Context context, android.util.AttributeSet attrs) {
super(context, attrs);
}
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, expandSpec);
}
}
use this
Finally i got the solution for the scrolling issue i got custom scrollview class to manage the scrollview.
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.ScrollView;
public class VerticalScrollview extends ScrollView{
public VerticalScrollview(Context context) {
super(context);
}
public VerticalScrollview(Context context, AttributeSet attrs) {
super(context, attrs);
}
public VerticalScrollview(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
switch (action)
{
case MotionEvent.ACTION_DOWN:
Log.i("VerticalScrollview", "onInterceptTouchEvent: DOWN super false" );
super.onTouchEvent(ev);
break;
case MotionEvent.ACTION_MOVE:
return false; // redirect MotionEvents to ourself
case MotionEvent.ACTION_CANCEL:
Log.i("VerticalScrollview", "onInterceptTouchEvent: CANCEL super false" );
super.onTouchEvent(ev);
break;
case MotionEvent.ACTION_UP:
Log.i("VerticalScrollview", "onInterceptTouchEvent: UP super false" );
return false;
default: Log.i("VerticalScrollview", "onInterceptTouchEvent: " + action ); break;
}
return false;
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
super.onTouchEvent(ev);
Log.i("VerticalScrollview", "onTouchEvent. action: " + ev.getAction() );
return true;
}
}
Check the onMeasure source code of the ListView, you will find that if the height measureSpec is UNSPECIFIED then the height of the listview is the height of only one item plus some padding, see below:
if (heightMode == MeasureSpec.UNSPECIFIED) {
heightSize = mListPadding.top + mListPadding.bottom + childHeight +
getVerticalFadingEdgeLength() * 2;
}
So we can simplily change the height SpecMode to AT_MOST, and the height size is the left height size of its parent.
Below is the solution:
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
heightMeasureSpec = MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(heightMeasureSpec),
MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
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;
}
}
I am making a grid-based game that will be much larger than the screen, and the user would scroll around in it. I basically put a bunch on ImageViews inside of a custom class that extends a relative layout. The problem is that even though RelativeLayout.LayoutParams is set to the correct size I want (1280*1280). The images are crammed against the sides of the screen and don't extend past it. I have got the scrolling logic working, and when I scroll, I can see it is a rectangle of images the size of one screen. How can I make it so the images extend past the screen?
The class that extends a relative layout:
public class GameGrid extends RelativeLayout {
ImageView[][] subViews;
int rows=0, cols=0, cellsize=0;
int width, height;
//Dragging variables
float startX;
float startY;
float lastX;
float lastY;
boolean touching;
boolean dragging;
int clickedChild;
public GameGrid(Context context) {
super(context);
init();
}
public GameGrid(Context context, int rws, int cls, int clsze) {
super(context);
rows=rws;
cols=cls;
cellsize=clsze;
init();
}
public GameGrid(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public GameGrid(Context context, AttributeSet attrs, int defaultStyles) {
super(context, attrs, defaultStyles);
init();
}
public void init() {
rows=10;
cols=10;
cellsize=128;
startX = 0;
startY = 0;
lastX=0;
lastY=0;
touching = false;
dragging = false;
clickedChild = -1;
subViews = new ImageView[cols][rows];
setLayoutParams(new RelativeLayout.LayoutParams(cellsize*cols,cellsize*rows));
width=this.getLayoutParams().width;
height=this.getLayoutParams().height;
this.setMinimumWidth(width);
this.setMinimumHeight(height);
Log.i("info","****************");
Log.i("info","GameGrid Made.");
Log.i("info","width: "+width+"\nheight: "+height);
Log.i("info","****************");
makeGrid();
// this.setOnTouchListener()
}
public boolean getDragging(){
return dragging;
}
public void makeGrid() {
for(int y=0;y<rows;y++){
for(int x=0;x<cols;x++){
ImageView temp = new ImageView(getContext());
temp.setImageResource(R.drawable.water1);
temp.setScaleType(ImageView.ScaleType.FIT_XY);
RelativeLayout.LayoutParams temp2 = new RelativeLayout.LayoutParams(width/cols,height/rows);
if (x == 0 && y == 0){ //If this is the first view being made, set it relative to the parent.
temp2.addRule(RelativeLayout.ALIGN_PARENT_TOP);
temp2.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
}
else if (x == 0){ //If this is in the first column, set it below the one above.
temp2.addRule(RelativeLayout.ALIGN_LEFT,subViews[0][y-1].getId());
temp2.addRule(RelativeLayout.BELOW,subViews[0][y-1].getId());
}
else { //Align the bottom with first one of that row.
temp2.addRule(RelativeLayout.RIGHT_OF,subViews[x-1][y].getId());
temp2.addRule(RelativeLayout.ALIGN_BOTTOM,subViews[0][y].getId());
}
temp.setLayoutParams(temp2);
subViews[x][y]=temp;
subViews[x][y].setId(x+y*cols+1);
// Toast.makeText(getContext(), "" + v.getId(), Toast.LENGTH_SHORT).show();
subViews[x][y].setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View v,MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN)
clickedChild = v.getId();
return false;
}
});
addView(temp);
}
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN)
{ // when the user touches the screen
startX = event.getX();
startY = event.getY();
lastX = event.getX();
lastY = event.getY();
touching = true;
dragging = false;
return true;
}
else if (event.getAction() == MotionEvent.ACTION_MOVE)
{ // when the user moves the touch
if (!dragging)
dragging = true;
int distX = (int)(event.getX()-lastX);
int distY = (int)(event.getY()-lastY);
this.scrollBy(-distX, -distY);
lastX = event.getX();
lastY = event.getY();
return true;
}
else if (event.getAction() == MotionEvent.ACTION_UP)
{ // when the user lifts the touch
if (!dragging){
if (clickedChild>0){
Toast.makeText(getContext(), "getHeight()= " + getHeight(), Toast.LENGTH_SHORT).show();
clickedChild = -1;
}
}
touching = false;
dragging = false;
return true;
}
else if (event.getAction() == MotionEvent.ACTION_CANCEL)
{ // if something gets lost in translation
startX = 0;
startY = 0;
lastX=0;
lastY=0;
touching = false;
dragging = false;
return true;
}
return false;
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
this.setMeasuredDimension(parentWidth, parentHeight);
}
The Activity:
public class Attacktics2 extends Activity {
/** Called when the activity is first created. */
GameGrid grid;
int rows, cols, cellsize;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.main);
}
public void start(View view) {
view.setVisibility(View.GONE);
grid = new GameGrid(this,10,10,128);
setContentView(grid);
}
}
Since you're already doing the heavy lifting of managing all the scrolling, I'd suggest that you implement your entire layout logic yourself and not rely on RelativeLayout. Except for ScrollView and HorizontalScrollView, the stock layout classes are going to restrict their children to be within the parent bounds. Those, in turn, will be restricted to the screen dimensions. If you handle the layout logic yourself, you can position child views so that they extend off screen. It then forms a viewport into a larger grid and can just render those children that are visible within the viewport.