So, I was going to create a custom view in order to get a photo album like tinder, a scrollview with autoadjust at photos positions. I thought an horizontalscrollview with a couple of tweaks would do the job. Added a 5*LayoutWidth horizontal LinerLayout to the scrollView sw and defined its onTouchListener like this:
sw.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
Log.d("MotionEvent", String.valueOf( event.getAction()));
if(event.getAction() == MotionEvent.ACTION_UP || event.getAction() == MotionEvent.ACTION_CANCEL){
int scrollX=sw.getScrollX();
Log.d("scrollX", String.valueOf( scrollX));
//Log.d("layoutWidth", String.valueOf( layoutWidth));
int aux = Math.round((float)scrollX/layoutWidth)*layoutWidth;
Log.d("correction", String.valueOf( aux));
sw.setScrollX(aux );
}
return false;
}
});
It is working just as expected, focusing on one of the 5 photos when releasing the touch input , but when I scroll and relese by fast gestures the scrollview is just ignoring sw.setScrollX(aux) and keeps scrolling. Can I do something to avoid this automatic scroll after release¿ Can I somehow manipulate the data of the input event¿
Thanks!
Finally I got the solution. I extended the HorizontalcrollView like that:
public class stepScrollView extends HorizontalScrollView {
public stepScrollView(Context context) {
super(context);
}
public stepScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public stepScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_UP:
Display d = ((Activity) getContext()).getWindowManager().getDefaultDisplay();
Point size = new Point();
d.getSize(size);
int layoutWidth = size.x;
int aux = Math.round((float)this.getScrollX()/layoutWidth)*layoutWidth;
this.setScrollX(aux );
return false;
default:
return super.onTouchEvent(ev);
}
}
}
it works with all the images you want to put in a Horizontal LinearLayout inner this custom view. You also have to resize your images to the stepScrollView horizontal dimension. In my case layoutWidth.
Related
I have an ImageView in a FrameLayout, I want to setup LongClickListener but its failing to work, I tried setting up OnTouchListener and its working flawless, I do not have the slightest idea as to why its not working but below is my code code:
public class DragImageView extends FrameLayout implements View.OnLongClickListener {
ImageView ivDrag;
public DragImageView(Context context) {
super(context);
}
public DragImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public DragImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#TargetApi(Build.VERSION_CODES.HONEYCOMB)
public void AddImageView(View draggableObject, int x, int y, int width, int height) {
LayoutParams lpDraggableView = new LayoutParams(width, height);
lpDraggableView.gravity = Gravity.TOP;
lpDraggableView.leftMargin = x;
lpDraggableView.topMargin = y;
if(draggableObject instanceof ImageView) {
this.ivDrag = (ImageView) draggableObject;
ivDrag.setLayoutParams(lpDraggableView);
ivDrag.setClickable(true);
ivDrag.setLongClickable(true);
ivDrag.setOnLongClickListener(this);
this.addView(ivDrag);
}
}
/**
* Draggable object ontouch listener
* Handle the movement of the object when dragged and dropped
*/
private View.OnTouchListener OnTouchToDrag =new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
FrameLayout.LayoutParams dragParam = (LayoutParams) v.getLayoutParams();
switch(event.getAction())
{
case MotionEvent.ACTION_MOVE:
{
dragParam.topMargin = (int)event.getRawY() - (v.getHeight());
dragParam.leftMargin = (int)event.getRawX() - (v.getWidth()/2);
v.setLayoutParams(dragParam);
break;
}
case MotionEvent.ACTION_UP:
{
dragParam.height = v.getHeight();
dragParam.width = v.getWidth();
dragParam.topMargin = (int)event.getRawY() - (v.getHeight());
dragParam.leftMargin = (int)event.getRawX() - (v.getWidth()/2);
v.setLayoutParams(dragParam);
break;
}
case MotionEvent.ACTION_DOWN:
{
dragParam.height = v.getHeight();//fixed on drag and drop
dragParam.width = v.getWidth();
v.setLayoutParams(dragParam);
break;
}
}
return true;
}
};
#Override
public boolean onLongClick(View view) {
ivDrag.setOnTouchListener(OnTouchToDrag);
Toast.makeText(view.getContext(), "OnLongClick", Toast.LENGTH_SHORT).show();
return false;
}
}
I want to setup LongClickListener but its failing to work
You are not receiving the callbacks from OnLongClickListener because it has no set listener. Since your class implements View.OnLongClickListener and you want to receive the callback in your overridden onLongClick() method, add this class itself as the listener and it will work. I've done so in the constructor (choose the appropriate constructor out of the three as per your initialization of the view):
public DragImageView(Context context, AttributeSet attrs) {
super(context, attrs);
this.setOnLongClickListener(this); // <-- set this class instance as the listener
}
Although I'm surprised how you got it working with OnTouchListener. You probably explicitly added the listener, right?
I need to add a RecyclerView in ScrollView.
The RecyclerView should wrap its contents and shouldn't be be scrollable, but the entire scroll view should be scrollable.
Can this be done an how?
If you look at the Recycler view adpater class
you will be having getITemViewType function
from that you can manage to put you scrollview content in one type of item
#Override
public int getItemViewType(int position) {
if (position == 0) {
return VIEW_TYPE_HEADER1;
} else if (position == 1) {
return VIEW_TYPE_HEADER2;
} else {
return VIEWTYEPITEM;
}
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(
final ViewGroup viewGroup, int viewType) {
if (viewType == VIEWTYEPITEM) {
//return item view
} else if (viewType == VIEW_TYPE_HEADER2) {
//return item view 2
} else {
//return item view 1
}
}
Main purpose of RecyclerView (as well as of old ListView and GridView) is performance optimization in cases when you have so many views in the list, that they do not fit on the screen and therefore are scrollable.
If your RecyclerView is so small that there are not enough views to fill it or there is some constant (and small) amount of views - then you don't need to use it at all. You won't win anything. Moreover, when you have one scrollable view inside of another scrollable view - how do you expect it to work? Which view should scroll when? It is ambiguous that's why it is not possible to do.
On the other hand, if you have a lot of views, then you better of using just RecyclerView without a ScrollView. In such situation it's common to add some sort of header or footer views which are arbitrarily big. Since RecyclerView is already scrollable, they will work as you want it to work. #SHASHIDHAR MANCHUKONDA exlained this idea in his answer to your question.
Stop the scrolling of the ScrollView. Is is perfect upon 5.0.
public class UnScrollView extends ScrollView{
private int downX;
private int downY;
private int mTouchSlop;
public UnScrollView(Context context) {
super(context);
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
}
public UnScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
}
public UnScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
}
#Override
public boolean onInterceptTouchEvent(MotionEvent e) {
int action = e.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
downX = (int) e.getRawX();
downY = (int) e.getRawY();
break;
case MotionEvent.ACTION_MOVE:
int moveY = (int) e.getRawY();
if (Math.abs(moveY - downY) > mTouchSlop) {
return true;
}
}
return super.onInterceptTouchEvent(e);
}
}
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);
}
I am Having a 10 Image Views Inside a Scroll View
once the Image is clicked I want to perform an action
But when I am trying to scroll the Imags in the ScrollView Touch_down and Touch_UP together are considered as a click
Help me out
I know the solution is easy
But I think I am missing some Logic
I am Putting My Code Here
public class CustomScrollView extends ScrollView {
public CustomScrollView(Context context) {
super(context);
}
public CustomScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomScrollView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return false;
}
#Override
public boolean onTouchEvent(MotionEvent p_event) {
if (p_event.getAction() == MotionEvent.ACTION_MOVE && getParent() != null) {
getParent().requestDisallowInterceptTouchEvent(true);
}
return super.onTouchEvent(p_event);
}
}
In this ScrollView I have added an ImageView
well, i'm gonna try to answer..
i think (in my opinion), you should use onclick only for the ImageView, so when you scrolling on the ScrollView, that ImageView won't get clicked..
here a sample on my code
on the layout.xml
<ImageView
android:id="#+id/ur_img_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="true"
android:contentDescription="#string/description"
android:onClick="onClickEvent"
android:src="#drawable/yourDrawable"
/>
then create the event on your activity.java import android.view.View, create a method similar to the one you use in the .xml file, (on the example android:onClick="onClickEvent")
public void onClickEvent(View v){
//do your event here
}
that's it.
I made a custom background for a button and also for different button states. But now I made to a point that I cannot understand.
When button is in normal state then it looks just fine. But when I press the button, I need to move text down few pixels because button background image moving (actually it feels like it moving on the image, because first there's border under the button and when it's in pressed state then this border disappears). Please see image below.
How can I move the buttons text in the button when buttons state is pressed? (maybe padding somehow or layout custom for a button)
Setting padding in 9-patch didn't work for me.
Setting padding in touch listeners is messy, would litter code anywhere buttons are used.
I went with subclassing Button, and it turned out reasonably tidy. In my case, I wanted to offset icon (drawableLeft) and text 1px left and 1px down.
Subclassed button widget:
package com.myapp.widgets;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.Button;
import com.myapp.R;
public class OffsetButton extends Button {
public OffsetButton(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public OffsetButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
public OffsetButton(Context context) {
super(context);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
boolean value = super.onTouchEvent(event);
if (event.getAction() == MotionEvent.ACTION_UP) {
setBackgroundResource(R.drawable.btn_normal);
} else if (event.getAction() == MotionEvent.ACTION_DOWN) {
setBackgroundResource(R.drawable.btn_pressed);
setPadding(getPaddingLeft() + 1, getPaddingTop() + 1, getPaddingRight() - 1,
getPaddingBottom() - 1);
}
return value;
}
}
And use it in layout like this:
<com.myapp.widgets.OffsetButton
android:text="#string/click_me"
android:drawableLeft="#drawable/icon_will_be_offset_too_thats_good" />
Few notes:
I did not use StateListDrawable for background, and am instead switching backgrounds in code. When I tried using StateListDrawable, there would be small pause between padding change and background change. That didn't look good.
Setting background resets padding, so don't need to adjust padding in ACTION_UP case
It was important to increase top and left padding, and at the same time decrease bottom and right padding. So the size of content area stays the same and content area is effectively just shifted.
I did not try it myself but if you use nine-patch as a background drawable for both states then you should consider setting proper padding box in pressed state drawable. See details here.
You can use padding on the view. You can add an OnTouchListener to the button or view like
viewF.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction == MotionEvent.ACTION_UP) {
//set your padding
} else if (event.getAction == MotionEvent.ACTION_DOWN){
//set your padding
}
return true;
}
});
The ontouchlistener will let your know when the button is pressed and not.
The Pēteris Caune answer works worse than overriding setPressed(). But everithing OK with setPressed until you test your app at ICS or lower device and add button as listview item.
To archieve this I've improved my button's class:
public class OffsetButton extends Button {
private static final int OFFSET_IN_DP = 6;
private int offset_in_px;
private boolean wasPressed = false;
private Integer[] defaultPaddings;
public OffsetButton(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initView();
}
public OffsetButton(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
}
public OffsetButton(Context context) {
super(context);
initView();
}
private void initView() {
offset_in_px = (int) DisplayUtil.convertDpToPixel(OFFSET_IN_DP);
}
#Override
public void setPressed(boolean pressed) {
if (pressed && !wasPressed) {
changePaddings();
}
if (!pressed && wasPressed) {
resetPaddings();
}
super.setPressed(pressed);
}
private void changePaddings() {
defaultPaddings = new Integer[]{getPaddingLeft(), getPaddingTop(), getPaddingRight(), getPaddingBottom()};
setPadding(getPaddingLeft(), getPaddingTop() + offset_in_px, getPaddingRight(), getPaddingBottom() - offset_in_px);
wasPressed = true;
}
private void resetPaddings() {
setPadding(defaultPaddings[0], defaultPaddings[1], defaultPaddings[2], defaultPaddings[3]);
wasPressed = false;
}
#Override
public boolean performClick() {
resetPaddings();
return super.performClick();
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (isEnabled())
{
if (event.getAction() == MotionEvent.ACTION_DOWN && !wasPressed) {
changePaddings();
} else if (event.getAction() == MotionEvent.ACTION_UP && wasPressed) {
resetPaddings();
}
}
return super.onTouchEvent(event);
}
}
this might just work:
setPadding(left, top, right, bottom); // Normal
setPadding(left, top + x, right, bottom - x); // Pressed