android handle both scroll and click events on a TextView - android

I have a problem with my android app. I'm using a simple Textview with a vertical scrollBar to display lyrics from a song. The problem is that in my activity I set a Onclick event on this same Textview. So when I scroll the lyrics in the textview, the activity registers a click event when I release my finger from the screen. I don't want the onClick event to happen after I scroll.
Here is what I have done so far but it does not work really well since im using a onLongClick event wich is not precise enough:
public class NowPlayingActivity extends Activity implements ckListener,OnLongClickListener
{
private TextView lyrics;
private static final String TAG_LYRICS = "LYRICS";
#Override
protected void onCreate(Bundle savedInstanceState)
{
this.lyrics = (TextView) this.findViewById(R.id.now_playing_Lyrics);
this.lyrics.setOnClickListener(this);
this.lyrics.setMovementMethod(new ScrollingMovementMethod());
this.lyrics.setOnLongClickListener(this);
}
public void onClick(View v)
{
String tag = (String) v.getTag();
if (tag.equals(NowPlayingActivity.TAG_LYRICS))
{
if (this.scrolled) //this way, the click action doesnt occur after a scroll
{
this.scrolled = false;
}
else
{
this.scrolled = false;
this.artwork.setVisibility(View.VISIBLE);
this.lyrics.setVisibility(View.GONE);
}
}
public boolean onLongClick(View arg0)
{
this.scrolled = true;
return this.scrolled;
}
what can I do to make it more "accurate" (so I dont have to make a longClick for it to work)
thanks!

Put your textview inside scrollview.
<ScrollView
android:id="#+id/content_scroll"
android:layout_width="fill_parent"
android:layout_height="match_parent"
android:layout_margin="7dip"
android:scrollbars="none" >
<TextView
android:id="#+id/fileContent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5dip" />
</ScrollView>
Then it should work properly. Hope it helps

Related

Android onClickListener for a whole layout with a different onClickListener for an internal view

I have a simple LinearLayout that consists of a TextView and an EditText. The behaviour that I'd like to achieve is to be able to click on the EditText and handle it like normal, but treat the encompassing LinearLayout as a button that launches a new activity.
So for example, if the user clicks the space around the button in the view, a new activity is launched. If the user clicks on the EditText, then the keyboard appears and the user can populate the EditText.
Here is the simple onClickListener for the layout, which simply states that it has been clicked:
LinearLayout test = (LinearLayout)findViewById(R.id.linearLayout1);
test.setOnClickListener(new OnClickListener(){
#Override
public void onClick(View arg0) {
System.out.println("layout clicked");
}
});
And the EditText has an OnFocusChangeListener that will simply state when it has gotten focus:
#Override
public void onFocusChange(View v, boolean hasFocus) {
System.out.println("EditText clicked");
}
Results:
-When the user clicks on the layout, the result "layout clicked" is correct
-When the user clicks on the edittext, the result is "layout clicked" followed by "EditText clicked", which is not correct. I'd like to ignore the linear layout's onClick event for this case.
Try creating a FrameLayout that contains a LinearLayout containing the TextView and place the EditText above the LinearLayout. This way you will not need to change anything about the listeners.
So it would be like this:
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent" >
<LinearLayout
android:id="#+id/linearLayout1"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/hello_world" />
</LinearLayout>
<EditText
android:id="#+id/editText1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="200dp" />
</FrameLayout>
Note: use margins to adjust the position of the EditText
I guess something like this should work, although it isn't the cleanest solution.
Declare a runnable that should be executed when the layout is clicked.
final Handler handler = new Handler();
Runnable layoutPressed = new Runnable() {
public void run() {
// LAYOUT CLICKED
}
};
Then start this runnable in your layout onClickListener.
LinearLayout test = (LinearLayout)findViewById(R.id.linearLayout1);
test.setOnClickListener(new OnClickListener(){
#Override
public void onClick(View arg0) {
handler.postDelayed(layoutPressed, 100);
}
});
Cancel the runnable in onFocusChange.
#Override
public void onFocusChange(View v, boolean hasFocus) {
handler.removeCallbacks(layoutPressed);
// EditText clicked
}
So whenever you click the editText, the onClick of your layout will get called first, but the actions will get cancelled when onFocusChange of the editText is called.
When you click the layout, onClick will get called and will execute its actions with a 100 msec delay.
You might have to modify the delay of the runnable.

How to scroll a HorizontalScrollView on button click in android?

I have horizontal scrollview in my android app with Next and Previous buttons.I want to show the these buttons only when the scollview needs scrolling.ie,width of scrollview content exceeds display width.Also want to hide previous and Next buttons when reaching first and last items respectively.How to to next/previous items when click on these buttons?
main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/mainLayout"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#ffffff" >
<Button
android:id="#+id/btnPrevoius"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:text="Previous"
android:visibility="gone" />
<HorizontalScrollView
android:id="#+id/horizontalScrollView1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="5dip"
android:layout_marginRight="5dip"
android:layout_toLeftOf="#+id/btnNext"
android:layout_toRightOf="#+id/btnPrevoius"
android:fillViewport="true" >
<LinearLayout
android:id="#+id/linearLayout1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
</LinearLayout>
</HorizontalScrollView>
<Button
android:id="#+id/btnNext"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:text="Next"
android:visibility="gone" />
</RelativeLayout>
activity
public class SampleActivity extends Activity {
private static LinearLayout linearLayout;
private static HorizontalScrollView horizontalScrollView;
private static Button btnPrevious;
private static Button btnNext;
private static int displayWidth = 0;
private static int arrowWidth = 0;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
horizontalScrollView = (HorizontalScrollView) findViewById(R.id.horizontalScrollView1);
linearLayout = (LinearLayout) findViewById(R.id.linearLayout1);
btnPrevious = (Button) findViewById(R.id.btnPrevoius);
btnNext = (Button) findViewById(R.id.btnNext);
for (int i = 0; i < 15; i++) {
Button button = new Button(this);
button.setTag(i);
button.setText("---");
linearLayout.addView(button);
}
ViewTreeObserver vto = linearLayout.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
ViewTreeObserver obs = linearLayout.getViewTreeObserver();
obs.removeGlobalOnLayoutListener(this);
Display display = getWindowManager().getDefaultDisplay();
displayWidth = display.getWidth();
if (linearLayout.getMeasuredWidth() > (displayWidth - 40)) {
btnPrevious.setVisibility(View.VISIBLE);
btnNext.setVisibility(View.VISIBLE);
}
}
});
btnPrevious.setOnClickListener(listnerLeftArrowButton);
horizontalScrollView.setOnTouchListener(listenerScrollViewTouch);
}
private OnTouchListener listenerScrollViewTouch = new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
showHideViews();
return false;
}
};
private OnClickListener listnerLeftArrowButton = new OnClickListener() {
#Override
public void onClick(View v) {
horizontalScrollView.onKeyDown(KeyEvent.KEYCODE_DPAD_RIGHT, new KeyEvent(0, 0));
}
};
public static void showHideViews() {
int maxScrollX = horizontalScrollView.getChildAt(0).getMeasuredWidth()- displayWidth;
Log.e("TestProjectActivity", "scroll X = " +horizontalScrollView.getScrollX() );
Log.i("TestProjectActivity", "scroll Width = " +horizontalScrollView.getMeasuredWidth() );
Log.d("TestProjectActivity", "Max scroll X = " + maxScrollX);
if (horizontalScrollView.getScrollX() == 0) {
hideLeftArrow();
} else {
showLeftArrow();
}
if (horizontalScrollView.getScrollX() == maxScrollX) {
showRightArrow();
} else {
//hideRightArrow();
}
}
private static void hideLeftArrow() {
btnPrevious.setVisibility(View.GONE);
}
private static void showLeftArrow() {
btnPrevious.setVisibility(View.VISIBLE);
}
private static void hideRightArrow() {
btnNext.setVisibility(View.GONE);
}
private static void showRightArrow() {
btnNext.setVisibility(View.VISIBLE);
}
}
The 'maxScrollX' value is not correct for me.How to find maximum scrollvalue for this?
Thanks in Advance
This might come a bit late, but for anyone out there that will face this problem I suggest alternative solution(s).
First, use different component than HorizontalScrollView. Here are the options:
OPTION 1: Horizontal ListView - add this class to your project (create a separate package, something like com.yourproject.widgets). Also you'll need to create custom Adapter, see how that's done in this example. I suggest you create separate adapter class (exp. HorizontalListViewAdapter) and put it in already created com.yourproject.widgets package.
add this widget to your layout in the xml (put it between buttons that need to mimic the scrolling behavior) you'll need to add something like:
<com.yourproject.widgets.HorizontalListView
android:id="#+id/hList"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
reference this (along with the buttons) in the Activity/Fragment that utilizes the widget
HorizontalListView mHList = (HorizontalListView) findViewById (R.id.hList);
Button bPrevoius = (Button) findViewById (R.id.btnPrevoius);
Button bNext = (Button) findViewById (R.id.btnNext);
add onClickListeners to the Buttons. Use the scrollTo() function predefined in the HorizontalListView widget. As you can see in the code, it takes int dp value to scroll. Add positive values if you want to scroll in right (next), and use negative values if you want to scroll in left (previous):
bPrevoius.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
//value 500 is arbitrarily given. if you want to achieve
//element-by-element scroll you should get the width of the
//previous element dynamically or if the elements of the
//list have uniform width just put that value instead
mHList.scrollTo(-500);
//if it's the first/last element you can bPrevoius.setEnabled(false)
}
});
bNext.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
mHList.scrollTo(500);
}
});
OPTION 2: More up to date solution to this issue can be the new widget RecyclerView introduced in Android L (addition of android:scrollbars="vertical" seems that would do the trick; other than that should have conventional ListView behavior). For more info check the official documentation.
devu
Plz have a look at the following links
1) http://android-er.blogspot.in/2012/07/implement-gallery-like.html
2) http://androiddreamers.blogspot.in/2012/09/horizontal-scroll-view-example.html
3)http://code.google.com/p/mobyfactory-uiwidgets-android/
Let me know if u r facing any issues
Thanks
In titanium appcelerator, you can do this using scrollableView
var scrollableView = Ti.UI.createScrollableView({
showPagingControl:true,
scrollingEnabled: true,
top: 360
});
Then, you can run a loop of all images or any content that you have, and add them to this view.
for(loop) {
eval("var view"+i+"=Ti.UI.createView();");
profile_image = Ti.UI.createImageView({
image: result[0]['profile_image'],
left:15,
width:82,
height:104,
top: 0
});
eval("view"+i+".add(profile_image);");
eval("scrollableView.addView(view"+i+");");
}
mywin.add(scrollableView);

Android- How can I show text selection on textview?

I am implementing a epub reading app where I am using textview for showing text of epub. I want to select text from textview when user long presses on textview and then do multiple operations on selected text of textview like highlight etc..
So, How can I show those cursors to user to select text whatever user wants.
*I dont want to use EditText and make it look like textview. May be overriding textview is prefered.
*I have attached screenshot to explain what I am looking for-
This is asked long time ago, when I had this problem myself as well. I made a Selectable TextView myself for my own app Jade Reader. I've hosted the solution to GitHub. (The code at BitBucket ties to the application, but it's more complete and polished.)
Selectable TextView (on GitHub)
Jade Reader (on BitBucket)
Using the following code will make your TextView selectable.
package com.zyz.mobile.example;
import android.app.Activity;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
public class MainActivity extends Activity {
private SelectableTextView mTextView;
private int mTouchX;
private int mTouchY;
private final static int DEFAULT_SELECTION_LEN = 5;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// make sure the TextView's BufferType is Spannable, see the main.xml
mTextView = (SelectableTextView) findViewById(R.id.main_text);
mTextView.setDefaultSelectionColor(0x40FF00FF);
mTextView.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
showSelectionCursors(mTouchX, mTouchY);
return true;
}
});
mTextView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mTextView.hideCursor();
}
});
mTextView.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
mTouchX = (int) event.getX();
mTouchY = (int) event.getY();
return false;
}
});
}
private void showSelectionCursors(int x, int y) {
int start = mTextView.getPreciseOffset(x, y);
if (start > -1) {
int end = start + DEFAULT_SELECTION_LEN;
if (end >= mTextView.getText().length()) {
end = mTextView.getText().length() - 1;
}
mTextView.showSelectionControls(start, end);
}
}
}
It depends on the minimum Android version that you'd like to support.
On 3.0+, you have the textIsSelectable attribute on the TextView, which enables this behavior. E.g.:
<TextView android:id="#+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:padding="#dimen/padding_medium"
android:text="#string/hello_world"
android:bufferType="spannable"
android:textIsSelectable="true"
android:textSize="28dip"
tools:context=".MainActivity" />
Below that, you best bet is to use an EditText that looks and behaves like a TextView (apart from the slection thing). Or you can implement this feature yourself using spans.

ListView receives touches, but onItemClickListener does not receive clicks after a pause and resume

We generate several ListViews that hold info for a user to filter information in another fragment. It works fine, unless you pause and resume the app (say, backgrounding it, or locking the screen). Once you do that, the list can be scrolled, but not clicked.
List generating code:
private View addList(LayoutInflater inflater, ViewGroup container, final FilterValue.SearchCategory type, final String[] labels) {
ArrayAdapter<String> adapter = generateArrayAdapter(inflater, labels, type);
if(adapter == null) {
return null;
}
filterAdapters.add(adapter);
ListView list = (ListView) inflater.inflate(R.layout.on_demand_filter_list, container, false);
list.setAdapter(adapter);
list.setItemsCanFocus(false);
list.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
list.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
list.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(final View view, final MotionEvent motionEvent) {
LOG.d(TAG, "NO TOUCHING!");
return false; //To change body of implemented methods use File | Settings | File Templates.
}
});
list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
LOG.d(TAG, "onItemClick!");
CheckedTextView textView = (CheckedTextView) view.findViewById(R.id.title);
textView.toggle();
if (textView.isChecked()) {
filterValue.addToSelectedList(labels[i], type);
} else {
filterValue.removeFromSelectedList(labels[i], type);
}
}
});
list.setAdapter(adapter);
list.setVisibility(View.GONE);
filterListContainer.addView(list);
return list;
}
The onTouch listener only exists to ensure the Touch is received. (It is.) The DescendantFocusability appears to have no effect, this bug exists before and after it was added.
Each is tied to a button that shows or hides the list.
titleHeader.setOnClickListener(new View.OnClickListener() {
public void onClick(View clickedView) {
closeNetworkList();
closeGenreList();
titlesOpen = !titlesOpen;
ImageView indicator = (ImageView) clickedView.findViewById(R.id.filter_expansion_indicator_icon);
if (indicator != null) {
if (titlesOpen) {
indicator.setImageResource(R.drawable.arrow_filter_up);
} else {
indicator.setImageResource(R.drawable.arrow_filter_down);
}
}
if (titlesOpen) {
titlesListView.setVisibility(View.VISIBLE);
} else {
titlesListView.setVisibility(View.GONE);
}
}
});
Tapping this button to hide and then show the listView (which was generated with addList) resets something, and the items can be clicked again.
XML for an item row:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:focusable="false"
android:focusableInTouchMode="false"
android:padding="8dp"
android:orientation="horizontal">
<CheckedTextView
android:id="#+id/title"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textColor="#drawable/on_demand_filter_checked_text_sel"
android:gravity="center_vertical"
android:paddingLeft="76dp"
android:focusable="false"
android:focusableInTouchMode="false"
android:drawableLeft="#drawable/checkbox_sel"
android:drawablePadding="14dp"
style="#style/LargeRegular"/>
</LinearLayout>
The focusables are new additions, but neither worked. The problem occurred before they were added.
The ListView itself:
<ListView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/list"
android:layout_width="fill_parent"
android:layout_height="275dp"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:divider="#color/Transparent"
android:descendantFocusability="blocksDescendants"
android:cacheColorHint="#ffffff"/>
I am at my absolute wits' end. No one on my team has a sensible solution to this. It works fine, right up until you pause and resume. We do absolutely nothing that touches the views in resume or pause. Can anyone help? I can provide more detail as needed.
I had similar problem with my app (extended SurfaceView which lost touch events after resume) and resolved it by calling the setFocusable( true ) in the onResume() implementation. Apparently the view didn't get the focus and therefore did not receive the touch events. Not sure whether this is the case here, but worth trying.
Remembered that I had had a similar problem with fragment activities. I had a case when layout requests were blocked, they did not cause actual layout traverse.
I've fixed it in Enroscar library (BaseFragment class) with the following snippet of code in a fragment class:
#Override
public void onStart() {
// ... other staff ...
super.onStart();
/*
XXX I don't know the reason but sometimes after coming back here from other activity all layout requests are blocked. :(
It looks like some concurrency issue or a views tree traversal bug
*/
final View contentView = getActivity().findViewById(android.R.id.content);
if (contentView != null) {
final ViewParent root = contentView.getParent();
if (contentView.isLayoutRequested() && !root.isLayoutRequested()) {
if (DebugFlags.DEBUG_GUI) { Log.i("View", "fix layout request"); }
root.requestLayout();
}
}
}

Android: swapping two overlapping views

Can anyone tell me what's wrong with this implementation? All I want to do here is have two overlapping views that swap places when you tap the screen. Unless I'm just using it wrong, View.bringToFront() does nothing?
Below is all the code in my app. Note that I added padding to the 'backView' just to make sure the two were actually overlapping. Indeed I could see both on the screen. While tapping the top view does indeed trigger the onClick method, nothing visibly changes in response to the calls to bringToFront.
public class MainActivity extends Activity implements OnClickListener {
private ImageView frontView;
private ImageView backView;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
frontView = (ImageView) findViewById(com.example.R.id.FrontView);
backView = (ImageView) findViewById(com.example.R.id.BackView);
frontView.setOnClickListener(this);
backView.setOnClickListener(this);
backView.setPadding(10,0,0,0);
}
private boolean flag;
public void onClick(View v) {
if (!flag) {
backView.bringToFront();
}
else {
frontView.bringToFront();
}
flag = !flag;
}
}
and the corresponding layout:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<ImageView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="#+id/FrontView"
android:src="#drawable/front"
/>
<ImageView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="#+id/BackView"
android:src="#drawable/back"
/>
</RelativeLayout>
Maybe it's the layout I'm using? I'm not sure... I've tried FrameLayout and LinearLayout as well.
I would try swapping content views instead of ImageViews.
Put each imageView in a different layout and then it is easy:
public void onClick(View v) {
if (!flag) {
setContentView(R.layout.main_front);
frontView = (ImageView) findViewById(com.example.R.id.FrontView);
frontView.setOnClickListener(this);
}
else {
setContentView(R.layout.main_back);
backView = (ImageView) findViewById(com.example.R.id.BackView);
backView.setOnClickListener(this);
}
flag = !flag;
}
There are a couple of Components that you can use that do this for you.
ViewAnimator, ViewFlipper and ViewSwitcher. You can set the animations you require etc and they hand the rest.
here's one example.
http://www.androidpeople.com/android-viewflipper-example/
Given your example, do you have to call invalidate() on the parent after you've called bringToFront() ?

Categories

Resources