Android Listview gravity center of screen - android

I want to know how to adjust my listview activity_main_menu_listview in center of LinearLayout. I tried with gravity but its not work for me but when I give it margin so it worked. But i want it auto, so how can i do it?
Thanks
<com.entropy.slidingmenu2.layout.MainLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<!-- This holds our menu -->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical" >
<ListView
android:id="#+id/activity_main_menu_listview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#ffffff"
android:divider="#android:color/transparent"
android:cacheColorHint="#00000000" >
</ListView>
</LinearLayout>
<!-- This holds our content-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<!-- This acts as Actionbar -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#ffffff"
android:layout_marginTop="5dp"
android:layout_marginLeft="5dp"
android:orientation="horizontal" >
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="toggleMenu"
android:background="#drawable/menu_btn"
android:id="#+id/activity_main_content_button_menu" />
</LinearLayout>
<!-- This is where fragment will show up -->
<FrameLayout
android:id="#+id/activity_main_content_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</FrameLayout>
</LinearLayout>
MainLayout Code:
public class MainLayout extends LinearLayout {
// Duration of sliding animation, in miliseconds
private static final int SLIDING_DURATION = 500;
// Query Scroller every 16 miliseconds
private static final int QUERY_INTERVAL = 16;
// MainLayout width
int mainLayoutWidth;
// Sliding menu
private View menu;
// Main content
private View content;
// menu does not occupy some right space
// This should be updated correctly later in onMeasure
private static int menuRightMargin = 120;
// The state of menu
private enum MenuState {
HIDING,
HIDDEN,
SHOWING,
SHOWN,
};
// content will be layouted based on this X offset
// Normally, contentXOffset = menu.getLayoutParams().width = this.getWidth - menuRightMargin
private int contentXOffset;
// menu is hidden initially
private MenuState currentMenuState = MenuState.HIDDEN;
// Scroller is used to facilitate animation
private Scroller menuScroller = new Scroller(this.getContext(),
new EaseInInterpolator());
// Used to query Scroller about scrolling position
// Note: The 3rd paramter to startScroll is the distance
private Runnable menuRunnable = new MenuRunnable();
private Handler menuHandler = new Handler();
// Previous touch position
int prevX = 0;
// Is user dragging the content
boolean isDragging = false;
// Used to facilitate ACTION_UP
int lastDiffX = 0;
// Constructor
// 3 parameters constructor seems to be unavailable in 2.3
/*
public MainLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
*/
public MainLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MainLayout(Context context) {
super(context);
}
// Overriding LinearLayout core methods
// Ask all children to measure themselves and compute the measurement of this
// layout based on the children
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mainLayoutWidth = MeasureSpec.getSize(widthMeasureSpec);
menuRightMargin = mainLayoutWidth * 30 / 100;
// Nothing to do, since we only care about how to layout
}
// This is called when MainLayout is attached to window
// At this point it has a Surface and will start drawing.
// Note that this function is guaranteed to be called before onDraw
#Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
// Get our 2 child View
menu = this.getChildAt(0);
content = this.getChildAt(1);
// Attach View.OnTouchListener
content.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
return MainLayout.this.onContentTouch(v, event);
}
});
// Initially hide the menu
menu.setVisibility(View.GONE);
}
// Called from layout when this view should assign a size and position to each of its children
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
//Log.d("MainLayout.java onLayout()", "left " + left + " top " + top + " right " + right + " bottom " + bottom);
//Log.d("MainLayout.java onLayout()", "getHeight " + this.getHeight() + " getWidth " + this.getWidth());
// True if MainLayout 's size and position has changed
// If true, calculate child views size
if(changed) {
// Note: LayoutParams are used by views to tell their parents how they want to be laid out
//Log.d("MainLayout.java onLayout()", "changed " + changed);
// content View occupies the full height and width
LayoutParams contentLayoutParams = (LayoutParams)content.getLayoutParams();
contentLayoutParams.height = this.getHeight();
contentLayoutParams.width = this.getWidth();
// menu View occupies the full height, but certain width
LayoutParams menuLayoutParams = (LayoutParams)menu.getLayoutParams();
menuLayoutParams.height = this.getHeight();
menuLayoutParams.width = this.getWidth() - menuRightMargin;
}
// Layout the child views
menu.layout(left, top, right - menuRightMargin, bottom);
content.layout(left + contentXOffset, top, right + contentXOffset, bottom);
}
// Custom methods for MainLayout
// Used to show/hide menu accordingly
public void toggleMenu() {
// Do nothing if sliding is in progress
if(currentMenuState == MenuState.HIDING || currentMenuState == MenuState.SHOWING)
return;
switch(currentMenuState) {
case HIDDEN:
currentMenuState = MenuState.SHOWING;
menu.setVisibility(View.VISIBLE);
menuScroller.startScroll(0, 0, menu.getLayoutParams().width,
0, SLIDING_DURATION);
break;
case SHOWN:
currentMenuState = MenuState.HIDING;
menuScroller.startScroll(contentXOffset, 0, -contentXOffset,
0, SLIDING_DURATION);
break;
default:
break;
}
// Begin querying
menuHandler.postDelayed(menuRunnable, QUERY_INTERVAL);
// Invalite this whole MainLayout, causing onLayout() to be called
this.invalidate();
}
// Query Scroller
protected class MenuRunnable implements Runnable {
#Override
public void run() {
boolean isScrolling = menuScroller.computeScrollOffset();
adjustContentPosition(isScrolling);
}
}
// Adjust content View position to match sliding animation
private void adjustContentPosition(boolean isScrolling) {
int scrollerXOffset = menuScroller.getCurrX();
//Log.d("MainLayout.java adjustContentPosition()", "scrollerOffset " + scrollerOffset);
// Translate content View accordingly
content.offsetLeftAndRight(scrollerXOffset - contentXOffset);
contentXOffset = scrollerXOffset;
// Invalite this whole MainLayout, causing onLayout() to be called
this.invalidate();
// Check if animation is in progress
if (isScrolling)
menuHandler.postDelayed(menuRunnable, QUERY_INTERVAL);
else
this.onMenuSlidingComplete();
}
// Called when sliding is complete
private void onMenuSlidingComplete() {
switch (currentMenuState) {
case SHOWING:
currentMenuState = MenuState.SHOWN;
break;
case HIDING:
currentMenuState = MenuState.HIDDEN;
menu.setVisibility(View.GONE);
break;
default:
return;
}
}
// Make scrolling more natural. Move more quickly at the end
// See the formula here http://cyrilmottier.com/2012/05/22/the-making-of-prixing-fly-in-app-menu-part-1/
protected class EaseInInterpolator implements Interpolator {
#Override
public float getInterpolation(float t) {
return (float)Math.pow(t-1, 5) + 1;
}
}
// Is menu completely shown
public boolean isMenuShown() {
return currentMenuState == MenuState.SHOWN;
}
// Handle touch event on content View
public boolean onContentTouch(View v, MotionEvent event) {
// Do nothing if sliding is in progress
if(currentMenuState == MenuState.HIDING || currentMenuState == MenuState.SHOWING)
return false;
// getRawX returns X touch point corresponding to screen
// getX sometimes returns screen X, sometimes returns content View X
int curX = (int)event.getRawX();
int diffX = 0;
switch(event.getAction()) {
case MotionEvent.ACTION_DOWN:
//Log.d("MainLayout.java onContentTouch()", "Down x " + curX);
prevX = curX;
return true;
case MotionEvent.ACTION_MOVE:
//Log.d("MainLayout.java onContentTouch()", "Move x " + curX);
// Set menu to Visible when user start dragging the content View
if(!isDragging) {
isDragging = true;
menu.setVisibility(View.VISIBLE);
}
// How far we have moved since the last position
diffX = curX - prevX;
// Prevent user from dragging beyond border
if(contentXOffset + diffX <= 0) {
// Don't allow dragging beyond left border
// Use diffX will make content cross the border, so only translate by -contentXOffset
diffX = -contentXOffset;
} else if(contentXOffset + diffX > mainLayoutWidth - menuRightMargin) {
// Don't allow dragging beyond menu width
diffX = mainLayoutWidth - menuRightMargin - contentXOffset;
}
// Translate content View accordingly
content.offsetLeftAndRight(diffX);
contentXOffset += diffX;
// Invalite this whole MainLayout, causing onLayout() to be called
this.invalidate();
prevX = curX;
lastDiffX = diffX;
return true;
case MotionEvent.ACTION_UP:
//Log.d("MainLayout.java onContentTouch()", "Up x " + curX);
Log.d("MainLayout.java onContentTouch()", "Up lastDiffX " + lastDiffX);
// Start scrolling
// Remember that when content has a chance to cross left border, lastDiffX is set to 0
if(lastDiffX > 0) {
// User wants to show menu
currentMenuState = MenuState.SHOWING;
// No need to set to Visible, because we have set to Visible in ACTION_MOVE
//menu.setVisibility(View.VISIBLE);
//Log.d("MainLayout.java onContentTouch()", "Up contentXOffset " + contentXOffset);
// Start scrolling from contentXOffset
menuScroller.startScroll(contentXOffset, 0, menu.getLayoutParams().width - contentXOffset,
0, SLIDING_DURATION);
} else if(lastDiffX < 0) {
// User wants to hide menu
currentMenuState = MenuState.HIDING;
menuScroller.startScroll(contentXOffset, 0, -contentXOffset,
0, SLIDING_DURATION);
}
// Begin querying
menuHandler.postDelayed(menuRunnable, QUERY_INTERVAL);
// Invalite this whole MainLayout, causing onLayout() to be called
this.invalidate();
// Done dragging
isDragging = false;
prevX = 0;
lastDiffX = 0;
return true;
default:
break;
}
return false;
}
}

If you want to center an item, don't use a LinearLayout as these are meant for displaying a number of items in a row.
Use a RelativeLayout instead with property android:layout_centerInParent="true"
<!-- This holds our menu -->
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
>
<ListView
android:id="#+id/activity_main_menu_listview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#ffffff"
android:layout_centerInParent="true"
android:divider="#android:color/transparent"
android:cacheColorHint="#00000000" >
</ListView>
</RelativeLayout>

You have two problems going on.
Your parent LinearLayout is layout_width="wrap_content", which means that it only goes as far from the left edge of the screen as it needs to, not all the way to the right edge of the screen, which is what you want.
You need to tell your child ListView (activity_main_menu_listview) to center itself horizontally. The attribute you need is layout_gravity, not gravity. The layout_gravity attribute is for child views to say where within its parent it wants to be. The gravity attribute says where within the view itself (usually a TextView) the contents (usually the text) should go.
So what you need is this:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<ListView
android:id="#+id/activity_main_menu_listview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="#ffffff"
android:divider="#android:color/transparent"
android:cacheColorHint="#00000000" >
</ListView>
</LinearLayout>
Also, a couple of very minor points: 1) Google wants us to use match_parent instead of fill_parent (one of the stupidest deprecations in history); 2) You only need the xmlns attribute in the root view of the XML, not your ListView's parent.

Try this :
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="vertical" >
And make sure your listview's item layout also have central gravity.
I noticed you also used a custom view : com.entropy.slidingmenu2.layout.MainLayout
If its not working, please post both of those code.

Related

Moving in RelativeLayout android

I am making an app in which a RelativeLayout fits completely in the screen. I have than added a button which zooms in this layout using setScaleX() an setScaleY() to the layout. As a consequence (obviously) the whole layout does'nt fit in the screen anymore. So I first thought using a ScrollView to be able to move in the RelativeLayout, but this didn't work. Any suggestions? Thanks in advance.
Here is the xml code:
<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:id="#+id/layoutMain"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="phou.minesweeper.GameActivity">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="#+id/chronometer"
android:layout_below="#+id/textViewBest">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerHorizontal="true"
android:layout_centerInParent="true"
android:layout_centerVertical="true">
<RelativeLayout
android:id="#+id/grid"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerInParent="true"
android:layout_centerVertical="true"></RelativeLayout>
</ScrollView>
</RelativeLayout>
</RelativeLayout>
The RelativeLayout which i'm zooming is the one with id = grid.
The xml code is so short because all of the views are created in java.
As you are manipulating the view size in Java using setScaleX/Y, you probably need to implement an onTouchListener on the RelativeLayout to handle drag motion events to allow you to move the entire layout programatically.
Assuming the view you are scaling is #id/grid, a simple OnTouchListener would look something like this.
final View grid = findViewById(R.id.grid);
final Point dispSize = new Point();
getWindowManager().getDefaultDisplay().getSize(dispSize);
grid.setOnTouchListener(new OnTouchListener() {
private PointF mAnchor = new PointF();
private PointF mTouchPoint = new PointF();
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
mTouchPoint.set(motionEvent.getX(), motionEvent.getY());
switch (motionEvent.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
mAnchor.set(mTouchPoint);
return true;
case MotionEvent.ACTION_MOVE:
float dx = mTouchPoint.x - mAnchor.x;
float dy = mTouchPoint.y - mAnchor.y;
float left = grid.getX() + dx;
float right = left + grid.getWidth();
float top = grid.getY() + dy;
float bottom = top + grid.getHeight();
if (grid.getWidth() > dispSize.x) {
if (left > 0f) {
left = 0f;
}
if (right < dispSize.x) {
left += dispSize.x - right;
}
}
if (grid.getHeight() > dispSize.y) {
if (top > 0f) {
top = 0f;
}
if (bottom < dispSize.y) {
top += dispSize.y - bottom;
}
}
grid.setX(left);
grid.setY(top);
return true;
default:
return true;
}
}
});
Note that this OnTouchListener will only receive touch events that have NOT been intercepted by the content views of the RelativeLayout (which we don't see in the XML code). This means that the content views need to pass any unconsumed touch events up the hierarchy by returning false in their own touch/gesture listeners.

How to display popup instead of CAB when textview is selected?

I am making a reading app and it has a full screen activity.
When user selects the part of the text a contextual action bar appears with option of copy. This is the default behaviour. But this actionbar blocks the text under it so user cannot select it.
I want to show a popup window like below.
I tried returning false from onCreateActionMode but when i do this i can't select the text either.
I want to know if there is a standart way to achieve this as many reading applications use this design.
I don't know how Play Books achieves this, but you could create a PopupWindow and calculate where to position it based on the selected text using Layout.getSelectionPath and a little bit of math. Basically, we're going to:
Calculate the bounds of the selected text
Calculate the bounds and initial location of the PopupWindow
Calculate the difference between the two
Offset the PopupWindow to rest center horizontally/vertically above or below the selected text
Calculating the selection bounds
From the docs:
Fills in the specified Path with a representation of a highlight
between the specified offsets. This will often be a rectangle or a
potentially discontinuous set of rectangles. If the start and end are
the same, the returned path is empty.
So, the specified offsets in our case would be the start and end of the selection, which can be found using Selection.getSelectionStart and Selection.getSelectionEnd. For convenience, TextView gives us TextView.getSelectionStart, TextView.getSelectionEnd and TextView.getLayout.
final Path selDest = new Path();
final RectF selBounds = new RectF();
final Rect outBounds = new Rect();
// Calculate the selection start and end offset
final int selStart = yourTextView.getSelectionStart();
final int selEnd = yourTextView.getSelectionEnd();
final int min = Math.max(0, Math.min(selStart, selEnd));
final int max = Math.max(0, Math.max(selStart, selEnd));
// Calculate the selection outBounds
yourTextView.getLayout().getSelectionPath(min, max, selDest);
selDest.computeBounds(selBounds, true /* this param is ignored */);
selBounds.roundOut(outBounds);
Now that we have a Rect of the selected text bounds, we can choose where we want to place the PopupWindow relative to it. In this case, we'll center it horizontally along the top or bottom of the selected text, depending on how much space we have to display our popup.
Calculating the initial popup coordinates
Next we'll need to calculate the bounds of the popup content. To do this, we'll first need to call PopupWindow.showAtLocation, but the bounds of the View we inflate won't immediately be available, so I'd recommend using a ViewTreeObserver.OnGlobalLayoutListener to wait for them to become available.
popupWindow.showAtLocation(yourTextView, Gravity.TOP, 0, 0)
PopupWindow.showAtLocation requires:
A View to retrieve a valid Window token from, which just uniquely identifies the Window to place the popup in
An optional gravity, but in our case it'll be Gravity.TOP
Optional x/y offsets
Since we can't determine the x/y offset until the popup content is laid out, we'll just initially place it at the default location. If you try to call PopupWindow.showAtLocation before the View you pass in has been laid out, you'll receive a WindowManager.BadTokenException, so you may consider using a ViewTreeObserver.OnGlobalLayoutListener to avoid that, but it mostly comes up when you have text selected and rotate your device.
final Rect cframe = new Rect();
final int[] cloc = new int[2];
popupContent.getLocationOnScreen(cloc);
popupContent.getLocalVisibleRect(cbounds);
popupContent.getWindowVisibleDisplayFrame(cframe);
final int scrollY = ((View) yourTextView.getParent()).getScrollY();
final int[] tloc = new int[2];
yourTextView.getLocationInWindow(tloc);
final int startX = cloc[0] + cbounds.centerX();
final int startY = cloc[1] + cbounds.centerY() - (tloc[1] - cframe.top) - scrollY;
View.getLocationOnScreen will return us the x/y coordinates for the popup content.
View.getLocalVisibleRect will return us the bounds of the popup content
View.getWindowVisibleDisplayFrame will return us the offsets to accommodate for the action bar, if present
View.getScrollY will return us the y offset for whatever scroll container our TextView is in (ScrollView in my case)
View.getLocationInWindow will return us the y offset for our TextView, in case the action bar pushes it down a little
Once we've gotten all of the info we need, we can calculate the final starting x/y of the popup content and then use this to figure out the difference between them and the selected text Rect so we can PopupWindow.update to the new location.
Calculating the offset popup coordinates
// Calculate the top and bottom offset of the popup relative to the selection bounds
final int popupHeight = cbounds.height();
final int textPadding = yourTextView.getPaddingLeft();
final int topOffset = Math.round(selBounds.top - startY);
final int btmOffset = Math.round(selBounds.bottom - (startY - popupHeight));
// Calculate the x/y coordinates for the popup relative to the selection bounds
final int x = Math.round(selBounds.centerX() + textPadding - startX);
final int y = Math.round(selBounds.top - scrollY < startY ? btmOffset : topOffset);
If there's enough room to display the popup above the selected text, we'll put it there; otherwise, we'll offset it below the selected text. In my case, I have 16dp padding around my TextView, so that needs to be taken into account too. We'll end up with the final x and y location to offset the PopupWindow with.
popupWindow.update(x, y, -1, -1);
-1 here just represents the default width/height for we supplied for the PopupWindow, in our case it'll be ViewGroup.LayoutParams.WRAP_CONTENT
Listening for selection changes
We want the PopupWindow to update every time we change the selected text.
An easy way to listen for selection changes is to subclass TextView and provide a callback to TextView.onSelectionChanged.
public class NotifyingSelectionTextView extends AppCompatTextView {
private SelectionChangeListener listener;
public NotifyingSelectionTextView(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void onSelectionChanged(int selStart, int selEnd) {
super.onSelectionChanged(selStart, selEnd);
if (listener != null) {
if (hasSelection()) {
listener.onTextSelected();
} else {
listener.onTextUnselected();
}
}
}
public void setSelectionChangeListener(SelectionChangeListener listener) {
this.listener = listener;
}
public interface SelectionChangeListener {
void onTextSelected();
void onTextUnselected();
}
}
Listening for scroll changes
If you have a TextView in a scroll container like ScrollView, you may also want to listen for scroll changes so that you can anchor your popup while you're scrolling. An easy way to listen for those is to subclass ScrollView and provide a callback to View.onScrollChanged
public class NotifyingScrollView extends ScrollView {
private ScrollChangeListener listener;
public NotifyingScrollView(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
if (listener != null) {
listener.onScrollChanged();
}
}
public void setScrollChangeListener(ScrollChangeListener listener) {
this.listener = listener;
}
public interface ScrollChangeListener {
void onScrollChanged();
}
}
Creating an empty ActionMode.Callback
Like you mention in your post, we'll need to return true in ActionMode.Callback.onCreateActionMode in order for our text to remain selectable. But we'll also need to call Menu.clear in ActionMode.Callback.onPrepareActionMode in order to remove all the items you may find in an ActionMode for selected text.
/** An {#link ActionMode.Callback} used to remove all action items from text selection */
static final class EmptyActionMode extends SimpleActionModeCallback {
#Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
// Return true to ensure the text is still selectable
return true;
}
#Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
// Remove all action items to provide an actionmode-less selection
menu.clear();
return true;
}
}
Now we can use TextView.setCustomSelectionActionModeCallback to apply our custom ActionMode. SimpleActionModeCallback is a custom class that just provides stubs for ActionMode.Callback, kinda similar to ViewPager.SimpleOnPageChangeListener
public class SimpleActionModeCallback implements ActionMode.Callback {
#Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
return false;
}
#Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return false;
}
#Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
return false;
}
#Override
public void onDestroyActionMode(ActionMode mode) {
}
}
Layouts
This is the Activity layout we're using:
<your.package.name.NotifyingScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/notifying_scroll_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
<your.package.name.NotifyingSelectionTextView
android:id="#+id/notifying_text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="16dp"
android:textIsSelectable="true"
android:textSize="20sp" />
</your.package.name.NotifyingScrollView>
This is our popup layout:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#drawable/action_mode_popup_bg"
android:orientation="vertical"
tools:ignore="ContentDescription">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageButton
android:id="#+id/view_action_mode_popup_add_note"
style="#style/ActionModePopupButton"
android:src="#drawable/ic_note_add_black_24dp" />
<ImageButton
android:id="#+id/view_action_mode_popup_translate"
style="#style/ActionModePopupButton"
android:src="#drawable/ic_translate_black_24dp" />
<ImageButton
android:id="#+id/view_action_mode_popup_search"
style="#style/ActionModePopupButton"
android:src="#drawable/ic_search_black_24dp" />
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_margin="8dp"
android:background="#android:color/darker_gray" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageButton
android:id="#+id/view_action_mode_popup_red"
style="#style/ActionModePopupSwatch"
android:src="#drawable/round_red" />
<ImageButton
android:id="#+id/view_action_mode_popup_yellow"
style="#style/ActionModePopupSwatch"
android:src="#drawable/round_yellow" />
<ImageButton
android:id="#+id/view_action_mode_popup_green"
style="#style/ActionModePopupSwatch"
android:src="#drawable/round_green" />
<ImageButton
android:id="#+id/view_action_mode_popup_blue"
style="#style/ActionModePopupSwatch"
android:src="#drawable/round_blue" />
<ImageButton
android:id="#+id/view_action_mode_popup_clear_format"
style="#style/ActionModePopupSwatch"
android:src="#drawable/ic_format_clear_black_24dp"
android:visibility="gone" />
</LinearLayout>
</LinearLayout>
These are our popup button styles:
<style name="ActionModePopupButton">
<item name="android:layout_width">48dp</item>
<item name="android:layout_height">48dp</item>
<item name="android:layout_weight">1</item>
<item name="android:background">?selectableItemBackground</item>
</style>
<style name="ActionModePopupSwatch" parent="ActionModePopupButton">
<item name="android:padding">12dp</item>
</style>
Util
The ViewUtils.onGlobalLayout you'll see is just a util method for handling some ViewTreeObserver.OnGlobalLayoutListener boilerplate.
public static void onGlobalLayout(final View view, final Runnable runnable) {
final OnGlobalLayoutListener listener = new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
view.getViewTreeObserver().removeOnGlobalLayoutListener(this);
runnable.run();
}
};
view.getViewTreeObserver().addOnGlobalLayoutListener(listener);
}
Bringing it altogether
So, now that we've:
Calculated the selected text bounds
Calculated the popup bounds
Calculated the difference and determined the popup offsets
Provided a way to listen for scroll changes and selection changes
Created our Activity and popup layouts
Bringing everything together may look something like:
public class ActionModePopupActivity extends AppCompatActivity
implements ScrollChangeListener, SelectionChangeListener {
private static final int DEFAULT_WIDTH = -1;
private static final int DEFAULT_HEIGHT = -1;
private final Point currLoc = new Point();
private final Point startLoc = new Point();
private final Rect cbounds = new Rect();
private final PopupWindow popupWindow = new PopupWindow();
private final ActionMode.Callback emptyActionMode = new EmptyActionMode();
private NotifyingSelectionTextView yourTextView;
#SuppressLint("InflateParams")
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_action_mode_popup);
// Initialize the popup content, only add it to the Window once we've selected text
final LayoutInflater inflater = LayoutInflater.from(this);
popupWindow.setContentView(inflater.inflate(R.layout.view_action_mode_popup, null));
popupWindow.setWidth(WRAP_CONTENT);
popupWindow.setHeight(WRAP_CONTENT);
// Initialize to the NotifyingScrollView to observe scroll changes
final NotifyingScrollView scroll
= (NotifyingScrollView) findViewById(R.id.notifying_scroll_view);
scroll.setScrollChangeListener(this);
// Initialize the TextView to observe selection changes and provide an empty ActionMode
yourTextView = (NotifyingSelectionTextView) findViewById(R.id.notifying_text_view);
yourTextView.setText(IPSUM);
yourTextView.setSelectionChangeListener(this);
yourTextView.setCustomSelectionActionModeCallback(emptyActionMode);
}
#Override
public void onScrollChanged() {
// Anchor the popup while the user scrolls
if (popupWindow.isShowing()) {
final Point ploc = calculatePopupLocation();
popupWindow.update(ploc.x, ploc.y, DEFAULT_WIDTH, DEFAULT_HEIGHT);
}
}
#Override
public void onTextSelected() {
final View popupContent = popupWindow.getContentView();
if (popupWindow.isShowing()) {
// Calculate the updated x/y pop coordinates
final Point ploc = calculatePopupLocation();
popupWindow.update(ploc.x, ploc.y, DEFAULT_WIDTH, DEFAULT_HEIGHT);
} else {
// Add the popup to the Window and position it relative to the selected text bounds
ViewUtils.onGlobalLayout(yourTextView, () -> {
popupWindow.showAtLocation(yourTextView, TOP, 0, 0);
// Wait for the popup content to be laid out
ViewUtils.onGlobalLayout(popupContent, () -> {
final Rect cframe = new Rect();
final int[] cloc = new int[2];
popupContent.getLocationOnScreen(cloc);
popupContent.getLocalVisibleRect(cbounds);
popupContent.getWindowVisibleDisplayFrame(cframe);
final int scrollY = ((View) yourTextView.getParent()).getScrollY();
final int[] tloc = new int[2];
yourTextView.getLocationInWindow(tloc);
final int startX = cloc[0] + cbounds.centerX();
final int startY = cloc[1] + cbounds.centerY() - (tloc[1] - cframe.top) - scrollY;
startLoc.set(startX, startY);
final Point ploc = calculatePopupLocation();
popupWindow.update(ploc.x, ploc.y, DEFAULT_WIDTH, DEFAULT_HEIGHT);
});
});
}
}
#Override
public void onTextUnselected() {
popupWindow.dismiss();
}
/** Used to calculate where we should position the {#link PopupWindow} */
private Point calculatePopupLocation() {
final ScrollView parent = (ScrollView) yourTextView.getParent();
// Calculate the selection start and end offset
final int selStart = yourTextView.getSelectionStart();
final int selEnd = yourTextView.getSelectionEnd();
final int min = Math.max(0, Math.min(selStart, selEnd));
final int max = Math.max(0, Math.max(selStart, selEnd));
// Calculate the selection bounds
final RectF selBounds = new RectF();
final Path selection = new Path();
yourTextView.getLayout().getSelectionPath(min, max, selection);
selection.computeBounds(selBounds, true /* this param is ignored */);
// Retrieve the center x/y of the popup content
final int cx = startLoc.x;
final int cy = startLoc.y;
// Calculate the top and bottom offset of the popup relative to the selection bounds
final int popupHeight = cbounds.height();
final int textPadding = yourTextView.getPaddingLeft();
final int topOffset = Math.round(selBounds.top - cy);
final int btmOffset = Math.round(selBounds.bottom - (cy - popupHeight));
// Calculate the x/y coordinates for the popup relative to the selection bounds
final int scrollY = parent.getScrollY();
final int x = Math.round(selBounds.centerX() + textPadding - cx);
final int y = Math.round(selBounds.top - scrollY < cy ? btmOffset : topOffset);
currLoc.set(x, y - scrollY);
return currLoc;
}
/** An {#link ActionMode.Callback} used to remove all action items from text selection */
static final class EmptyActionMode extends SimpleActionModeCallback {
#Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
// Return true to ensure the yourTextView is still selectable
return true;
}
#Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
// Remove all action items to provide an actionmode-less selection
menu.clear();
return true;
}
}
}
Results
With the action bar (link to video):
Without the action bar (link to video):
Bonus - animation
Because we know the starting location of the PopupWindow and the offset location as the selection changes, we can easily perform a linear interpolation between the two values to create a nice animation when we're moving things around.
public static float lerp(float a, float b, float v) {
return a + (b - a) * v;
}
private static final int DEFAULT_ANIM_DUR = 350;
private static final int DEFAULT_ANIM_DELAY = 500;
#Override
public void onTextSelected() {
final View popupContent = popupWindow.getContentView();
if (popupWindow.isShowing()) {
// Calculate the updated x/y pop coordinates
popupContent.getHandler().removeCallbacksAndMessages(null);
popupContent.postDelayed(() -> {
// The current x/y location of the popup
final int currx = currLoc.x;
final int curry = currLoc.y;
// Calculate the updated x/y pop coordinates
final Point ploc = calculatePopupLocation();
currLoc.set(ploc.x, ploc.y);
// Linear interpolate between the current and updated popup coordinates
final ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
anim.addUpdateListener(animation -> {
final float v = (float) animation.getAnimatedValue();
final int x = Math.round(AnimUtils.lerp(currx, ploc.x, v));
final int y = Math.round(AnimUtils.lerp(curry, ploc.y, v));
popupWindow.update(x, y, DEFAULT_WIDTH, DEFAULT_HEIGHT);
});
anim.setDuration(DEFAULT_ANIM_DUR);
anim.start();
}, DEFAULT_ANIM_DELAY);
} else {
...
}
}
Results
With the action bar - animation (link to video)
Extra
I don't go into how to attach on click listeners to the popup actions and there are probably several ways to achieve this same effect with different calculations and implementations. But I will mention that if you wanted to retrieve the selected text and then do something with it, you'd just need to CharSequence.subSequence the min and max from the selected text.
Anyway, I hope this has been helpful! Let me know if you have any questions.

Android: child view outside of its parent doesn't respond to click events

For having effects I scale up the child view which cause child view to go out side of its parent view. I have a button in child view, it works before scaling but after scaling doesn't work. what is going wrong? see image below:
for scaling child I use this code:
childView.bringToFront();
Animation a = new Animation() {
#Override
protected void applyTransformation(float t, Transformation trans) {
float scale = 1f * ( 1 - t ) + SCALE_UP_FACTOR * t;
childView.setScaleX(scale);
childView.setScaleY(scale);
}
#Override
public boolean willChangeBounds() {
return true;
}
};
a.setDuration(ANIM_DURATION);
a.setInterpolator(new Interpolator() {
#Override
public float getInterpolation(float t) {
t -= 1f;
return (t * t * t * t * t) + 1f; // (t-1)^5 + 1
}
});
childView.startAnimation(a);
the parent is a ViewPager :
<ViewPager
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/invoice_list_view_pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#f5f5f5"
android:layout_gravity="center"
android:clipChildren="false"
android:clipToPadding="false"
/>
This should do the trick:
final View grandParent = (View) childView.getParent().getParent();
grandParent.post(new Runnable() {
public void run() {
Rect offsetViewBounds = new Rect();
childView.getHitRect(offsetViewBounds);
// After scaling you probably want to append your view to the new size.
// in your particular case it probably could be only offsetViewBounds.right:
// (animDistance - int value, which you could calculate from your scale logic)
offsetViewBounds.right = offsetViewBounds.right + animDistance;
// calculates the relative coordinates to the parent
((ViewGroup)parent).offsetDescendantRectToMyCoords(childView, offsetViewBounds);
grandParent.setTouchDelegate(new TouchDelegate(offsetViewBounds, childView));
}
});
Though I'm not sure whether it will work with Animation, but for scaling you could use something like that instead:
float scale = ...; // your scale logic
ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(childView,
PropertyValuesHolder.ofFloat("scaleX", scale),
PropertyValuesHolder.ofFloat("scaleY", scale));
animator.setDuration(ANIM_DURATION);
animator.start();
And pay attention to the line android:clipChildren="false" for your parent view in XML-file.

Android Floating activity with dismiss on swipe

The latest Facebook's android app has a very nice floating comment window. There the user can dismiss the window swiping up or down making it really ease to use.
I want to implement a similar behaviour in my app but I don't know how to do it. Any idea or clue about how to do it will be really appreciated.
Screenshots of the Facebook app
(sorry, the Facebook app from where I took the screenshots is in Japanese)
I write some code that match this closing/resizing behaviour, I don't know if it's the way to go but my code is based on Activity class. First thing I do is create an activity and give it Transluscenttheme to get an activity with transparent background.
In my manifest.xml :
<activity
android:name=".PopupActivity"
android:label="#string/title_activity_popup"
<!-- Use Translucent theme to get transparent activity background
and NoTitleBar to avoid super old style title bar ;) -->
android:theme="#android:style/Theme.Translucent.NoTitleBar">
</activity>
Then I create a simple layout file containing a textview (corresponding to Facebook tchatting part) and a view (corresponding to Facebook "Write your msg"/"send smiley" tab)
my layout/activity_popup.xml :
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/base_popup_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="8dp"
android:background="#android:color/darker_gray"
android:layout_marginBottom="124dp">
<TextView
android:text="#string/hello_world"
android:layout_width="match_parent"
android:layout_height="200dp"
android:background="#android:color/black"/>
<View
android:layout_width="match_parent"
android:layout_height="80dp"
android:layout_alignParentBottom="true"
android:background="#android:color/holo_blue_dark"/>
</RelativeLayout>
Finally I handle touch and move event in my PopupActivity class, I use onTouchListener which provide callback in onTouch method.
PopupActivity
public class PopupActivity extends Activity implements View.OnTouchListener{
private RelativeLayout baseLayout;
private int previousFingerPosition = 0;
private int baseLayoutPosition = 0;
private int defaultViewHeight;
private boolean isClosing = false;
private boolean isScrollingUp = false;
private boolean isScrollingDown = false;
#Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_popup);
baseLayout = (RelativeLayout) findViewById(R.id.base_popup_layout);
baseLayout.setOnTouchListener(this);
}
public boolean onTouch(View view, MotionEvent event) {
// Get finger position on screen
final int Y = (int) event.getRawY();
// Switch on motion event type
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
// save default base layout height
defaultViewHeight = baseLayout.getHeight();
// Init finger and view position
previousFingerPosition = Y;
baseLayoutPosition = (int) baseLayout.getY();
break;
case MotionEvent.ACTION_UP:
// If user was doing a scroll up
if(isScrollingUp){
// Reset baselayout position
baseLayout.setY(0);
// We are not in scrolling up mode anymore
isScrollingUp = false;
}
// If user was doing a scroll down
if(isScrollingDown){
// Reset baselayout position
baseLayout.setY(0);
// Reset base layout size
baseLayout.getLayoutParams().height = defaultViewHeight;
baseLayout.requestLayout();
// We are not in scrolling down mode anymore
isScrollingDown = false;
}
break;
case MotionEvent.ACTION_MOVE:
if(!isClosing){
int currentYPosition = (int) baseLayout.getY();
// If we scroll up
if(previousFingerPosition >Y){
// First time android rise an event for "up" move
if(!isScrollingUp){
isScrollingUp = true;
}
// Has user scroll down before -> view is smaller than it's default size -> resize it instead of change it position
if(baseLayout.getHeight()<defaultViewHeight){
baseLayout.getLayoutParams().height = baseLayout.getHeight() - (Y - previousFingerPosition);
baseLayout.requestLayout();
}
else {
// Has user scroll enough to "auto close" popup ?
if ((baseLayoutPosition - currentYPosition) > defaultViewHeight / 4) {
closeUpAndDismissDialog(currentYPosition);
return true;
}
//
}
baseLayout.setY(baseLayout.getY() + (Y - previousFingerPosition));
}
// If we scroll down
else{
// First time android rise an event for "down" move
if(!isScrollingDown){
isScrollingDown = true;
}
// Has user scroll enough to "auto close" popup ?
if (Math.abs(baseLayoutPosition - currentYPosition) > defaultViewHeight / 2)
{
closeDownAndDismissDialog(currentYPosition);
return true;
}
// Change base layout size and position (must change position because view anchor is top left corner)
baseLayout.setY(baseLayout.getY() + (Y - previousFingerPosition));
baseLayout.getLayoutParams().height = baseLayout.getHeight() - (Y - previousFingerPosition);
baseLayout.requestLayout();
}
// Update position
previousFingerPosition = Y;
}
break;
}
return true;
}
}
There are two small methods called when user has scroll enough to close popup (ie animate and finish activity) :
public void closeUpAndDismissDialog(int currentPosition){
isClosing = true;
ObjectAnimator positionAnimator = ObjectAnimator.ofFloat(baseLayout, "y", currentPosition, -baseLayout.getHeight());
positionAnimator.setDuration(300);
positionAnimator.addListener(new Animator.AnimatorListener()
{
. . .
#Override
public void onAnimationEnd(Animator animator)
{
finish();
}
. . .
});
positionAnimator.start();
}
public void closeDownAndDismissDialog(int currentPosition){
isClosing = true;
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
int screenHeight = size.y;
ObjectAnimator positionAnimator = ObjectAnimator.ofFloat(baseLayout, "y", currentPosition, screenHeight+baseLayout.getHeight());
positionAnimator.setDuration(300);
positionAnimator.addListener(new Animator.AnimatorListener()
{
. . .
#Override
public void onAnimationEnd(Animator animator)
{
finish();
}
. . .
});
positionAnimator.start();
}
With all this code your should be able to start PopupActivity that globally match Facebook popup behaviour. It's just a draft class and a lot of work remains to do : add animations, work on closing parameters and so on...
Screenshots :
I think you can use BottomSheetDialogFragment component from appcompat libarary. Check this article for the information:
https://medium.com/#nullthemall/new-bottomsheet-caab21aff19b#.gpu1l516z
You could also get useful information from
documentaion.
Well, the OP title was asking for a floating activity, but the OP content was looking for a floating comment window that is like facebook comment window.
So, here this will be implemented by a DialogFragment which provide us with the behavior of automatically bouncing the dialog to its original state/window size whenever you swipe up or down. And this behavior is kept when the dialog is swiped a little (exactly swiped a distance less than the half of the original layout size).
The remaining thing is it dismiss this dialog if its size is less than the half of the original window size; in other words, if it's swiped greater than the half of the original layout size. This part is adjusted from the accepted answer by changing the only the Y position of the root layout of the dialog window, and without changing the size of the window as this provided weird resizing behavior.
First create this style to have a transparent background to the dialog window:
<style name="NoBackgroundDialogTheme" parent="Theme.AppCompat.Light.Dialog">
<item name="android:windowBackground">#null</item>
</style>
This style will be applied in the DialogFragment by overriding getTheme() method.
And here is the customized DialogFragment:
class MyDialogFragment : DialogFragment(), View.OnTouchListener {
private var rootLayoutY: Int = 0
private val rootLayout by lazy {
requireView().findViewById<ConstraintLayout>(R.id.dialog_root)
}
private var oldY = 0
private var baseLayoutPosition = 0
private var defaultViewHeight = 0
private var isScrollingUp = false
private var isScrollingDown = false
override fun getTheme(): Int {
return R.style.NoBackgroundDialogTheme
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
val view: View = inflater.inflate(
R.layout.fragment_dialog_facebook_comment, container,
false
)
view.setBackgroundResource(R.drawable.rounded_background)
return view
}
override fun onStart() {
super.onStart()
// Making the dialog full screen
dialog?.window?.setLayout(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.MATCH_PARENT
)
}
#SuppressLint("ClickableViewAccessibility")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
rootLayout.setOnTouchListener(this)
rootLayout.viewTreeObserver.addOnGlobalLayoutListener(object :
OnGlobalLayoutListener {
override fun onGlobalLayout() {
rootLayout.viewTreeObserver
.removeOnGlobalLayoutListener(this)
// save default base layout height
defaultViewHeight = rootLayout.height
}
})
}
#SuppressLint("ClickableViewAccessibility")
override fun onTouch(v: View?, event: MotionEvent?): Boolean {
// Get finger position on screen
val y = event!!.rawY.toInt()
// Switch on motion event type
when (event.action and MotionEvent.ACTION_MASK) {
MotionEvent.ACTION_DOWN -> {
// Init finger and view position
oldY = y
baseLayoutPosition = rootLayout.y.toInt()
}
MotionEvent.ACTION_UP -> {
if (rootLayoutY >= defaultViewHeight / 2) {
dismiss()
return true
}
// If user was doing a scroll up
if (isScrollingUp) {
// Reset baselayout position
rootLayout.y = 0f
// We are not in scrolling up mode anymore
isScrollingUp = false
}
// If user was doing a scroll down
if (isScrollingDown) {
// Reset baselayout position
rootLayout.y = 0f
// Reset base layout size
rootLayout.layoutParams.height = defaultViewHeight
rootLayout.requestLayout()
// We are not in scrolling down mode anymore
isScrollingDown = false
}
}
MotionEvent.ACTION_MOVE -> {
rootLayoutY = abs(rootLayout.y.toInt())
// Change base layout size and position (must change position because view anchor is top left corner)
rootLayout.y = rootLayout.y + (y - oldY)
if (oldY > y) { // scrolling up
if (!isScrollingUp) isScrollingUp = true
} else { // Scrolling down
if (!isScrollingDown) isScrollingDown = true
}
// Update y position
oldY = y
}
}
return true
}
}
In my case, the root layout of the dialog is a ConstraintLayout.

implementing setOnClickListener

i have this sample and i am trying to Implement a Click on item(s) inside sub view
i have this two xml files
this is the subview.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">
<TextView
android:id="#+id/textLabel"
android:layout_height="fill_parent"
android:layout_width="fill_parent"
android:textSize="50dip"
android:textColor="#00FF00"
android:textStyle="bold"/>
</LinearLayout>
this is the scrollview.xml view:
<?xml version="1.0" encoding="utf-8"?>
<HorizontalScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/scrollview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:scrollbars="none">
<LinearLayout
android:layout_width="fill_parent"
android:id="#+id/scrollviewlinearlayout"
android:layout_height="fill_parent">
</LinearLayout>
</HorizontalScrollView>
and this is the Activity:
public class TestTwo extends Activity {
Context mContext;
HorizontalScrollView mScrollView;
LinearLayout mLinearLayout;
LinearLayout.LayoutParams mLinearLayoutParams;
Display mDisplay;
// scroll behaviour
private int mScrollStartPosition;
private static final float SCROLL_MARGIN = 0.2f;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mContext = this;
// load layout from xml and get references to sub-views
setContentView(R.layout.scrollview);
mScrollView = (HorizontalScrollView) findViewById(R.id.scrollview);
mLinearLayout = (LinearLayout) findViewById(R.id.scrollviewlinearlayout);
// get a display reference (used to find screen size)
mDisplay = ((WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
// get the layout parameters to apply to the sub-views
mLinearLayoutParams = new LayoutParams(mDisplay.getWidth(), mDisplay.getHeight());
// add some views to the ScrollView
addViewsToScrollView();
}
private void addViewsToScrollView() {
LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
for (int i = 0; i < 3; i++) {
// inflate view from xml
View child = inflater.inflate(R.layout.subview, null);
// give it a number
final TextView text = (TextView) child.findViewById(R.id.textLabel);
text.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
text.setText("Test");
}
});
text.setText("" + (i + 1));
// give it a colour
text.setBackgroundColor(Color.rgb((int) (Math.random() * 255), (int) (Math.random() * 255),
(int) (Math.random() * 255)));
// apply layout parameters, and add it
child.setLayoutParams(mLinearLayoutParams);
mLinearLayout.addView(child);
}
}
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
int viewWidth = mDisplay.getWidth(); // width of each view
int triggerWidth = (int) (SCROLL_MARGIN * viewWidth); // amount user has to scroll to move to next view
int pos = mScrollView.getScrollX();
int diff = pos % viewWidth; // offset of current scroll from leftmost view's snap position
int posLeftView = pos - diff; // absolute snap position of the leftmost view on screen
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
// Record the starting scroll position. This is used to decide the scroll direction.
mScrollStartPosition = pos;
break;
case MotionEvent.ACTION_UP:
if (pos > mScrollStartPosition) {
// Scrolling right
if (diff > triggerWidth) mScrollView.smoothScrollTo(posLeftView + viewWidth, 0);
else mScrollView.smoothScrollTo(posLeftView, 0);
} else {
// Scrolling left
if (diff > (viewWidth - triggerWidth)) mScrollView.smoothScrollTo(posLeftView + viewWidth, 0);
else mScrollView.smoothScrollTo(posLeftView, 0);
}
// replacing our scrollTo command with it's own
return true;
}
return super.dispatchTouchEvent(ev);
}
}
i tried to debug it and it seems its not fire the onClick event
can you help me please with this problem ?
You're doing a bunch of wild things that I'll leave for another time. Try text.setClickable(true) on your final TextView text
Ok sorry for all these edits. here are your problems though.
you shouldn't use the child statement
2. you shouldn't be setting an onclick listener for an item thats not within the current content
So......
final TextView text = (TextView) child.findViewById(R.id.textLabel);
should be
final TextView text = (TextView) findViewById(R.id.textLabel);
since the content needs to be set to the proper xml file
setContentView(R.layout.scrollview);
would need to be
setContentView(R.layout.<NAME OF SECOND XML FILE WHICH U DIDNT INCLUDE>);

Categories

Resources