I have a SpannableString set as the text of my TextView which is inside another view that has a click handler. I've got ClickableSpans inside my text which are, you are right, clickable.
My problem is that I need touch events to be captured in case of a click inside the clickable span and not to propagate to parent view (as the parent has another click handler).
The container view is simply a feed of posts in my app, and those posts can contain hashtags/mentions/links etc. If a user taps a hashtag, they should go to the hashtag which is handled by the clickable span, otherwise, they should go to the post, which is handled by the container itself.
Is there any straightforward mechanism to implement this functionality?
I've came up with a terrible, anti-pattern solution that works well.
In my app class, I've defined:
public static boolean shouldIgnoreNextTouchEvent = false;
In my ClickableSpans click handler, I've set a global flag to avoid next touch event to true:
ClickableSpan clickableSpan = new ClickableSpan() {
#Override
public void onClick(View widget) {
App.shouldIgnoreNextTouchEvent = true;
...
}
}
Then in my parent view's handler:
#Override
public void onClick() {
if(App.shouldIgnoreNextTouchEvent){
App.shouldIgnoreNextTouchEvent = false;
return;
}
...
}
I know it's not a good solution but it works like a charm.
Alternately,
Add a tag to the widget that generated the click event
`
ClickableSpan clickableSpan = new ClickableSpan() {
#Override
public void onClick(View widget) {
widget.setTag("hashtag"); // or anything else
}
}
`
In the parent, check if the tag is present. If it is, this has been consumed by the ClickableSpan
`
#Override
public void onClick(View view) {
String tag = (String) view.getTag();
if(tag.equals("hashtag"){
// Already consumed by child (ClickableSpan)
return;
}
// rest of your code ...
}
`
Related
I would like to link some text on a TextView to an Activity. This is the TextView that I have:
<TextView
android:id="#+id/termsLink"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#string/terms"
android:layout_weight="4"/>
where #string/terms is:
<string name="terms">Accept terms & conditions..</string>
If I had a link to a webpage I would do it like this:
TextView link = (TextView) findViewById(R.id.termsLink);
link.setMovementMethod(LinkMovementMethod.getInstance());
but I do not know how to start an Activity when I press the link as when it is a real link (that it links a webpage).
EDIT: Please note that I do not have to handle the onClick event in the full text because the link is only on the part "terms & conditions".
EDIT 2: I have tried using two TextView as suggested on the comments and one of the answers below to make the same effect. But sometimes (depending on the screen) the "terms & conditions" part occupy two lines because it does not fill properly on the available space so the second line it is shown on the second TextView and not on the begining of the second line.
The effect is similar to this:
Accept terms &
conditions.
and I would like that it would be like this:
Accept terms &
conditions.
Thanks in advance!
Create a helper class with inner onClick listener
public class ClickSpan extends ClickableSpan {
private String url;
private OnClickListener listener;
public ClickSpan(String url, OnClickListener listener) {
this.url = url;
this.listener = listener;
}
#Override
public void onClick(View widget) {
if (listener != null) listener.onClick(url);
}
public interface OnClickListener {
void onClick(String url);
}
}
Then convert existing span into clickable one
public static Spannable createClickableSpans(Spanned original, ClickSpan.OnClickListener listener) {
SpannableString result = new SpannableString(original);
URLSpan[] spans = result.getSpans(0, result.length(), URLSpan.class);
for (URLSpan span : spans) {
int start = result.getSpanStart(span);
int end = result.getSpanEnd(span);
int flags = result.getSpanFlags(span);
result.removeSpan(span);
result.setSpan(new ClickSpan(span.getURL(), listener), start, end, flags);
}
return result;
}
So, final usage would be like
TextView link = (TextView) findViewById(R.id.termsLink);
link.setMovementMethod(LinkMovementMethod.getInstance());
link.setText(createClickableSpans((Spanned)link.getText(), new ClickSpan.OnClickListener(){
#Override
public void onClick(String url){
//Handle URL on text view click
}
}));
To make only part of a TextView clickable, you can use a spannable inside the TextView and set an onClick event listener. From here, you launch the activity with an intent as usual. You can limit the clickable section of the text by specifying the character positions (start to end)
Checkout this answer by #becomputer06
How to set the part of the text view is clickable
You should probably separate the text into 2 text views one with the terms and condition and one with just the accept.It would make things cleaner and easier. The following TextView is assuming its just for accept.
In the layouts corresponding java class(example: activity_main -> MainActivity):
public void start_activity(View view){
Intent newActivityIntent = new Intent(this,NewActivity.class);
startActivity(newActivityIntent);
}
NewActivity.class is just the name of the activity you want to start.
public class MainActivity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) {
....
}
public void start_activity(View view){
.....
}
}
onClick method is called twice, so when back from SecondActivity.class it will again reload it. I have a TextView named postTextView in which See More is clickable.
Where:
R.string.readMore = See More.
Here is the code which I've used.
String mTitleBody = Html.fromHtml(postBodyText).toString().substring(0, 150).trim();
mTitleBody = mTitleBody.concat("..." + mContext.getResources().getString(R.string.readMore)).replaceAll("<img.+?>|<IMG.+?>", "").replaceAll("\n", "<br/>");
int index1 = Html.fromHtml(mTitleBody).toString().trim().length() -
mContext.getResources().getString(R.string.readMore).length();
int index2 = Html.fromHtml(mTitleBody).toString().trim().length();
postTextView.setTextIsSelectable(true);
postTextView.setMovementMethod(LinkMovementMethod.getInstance());
postTextView.setText(Html.fromHtml(mTitleBody), TextView.BufferType.SPANNABLE);
Spannable mySpannable = (Spannable) postTextView.getText();
ClickableSpan myClickableSpan = new ClickableSpan() {
#Override
public void onClick(View view) {
Log.d("FirstClass", "onClick");
Intent intent = new Intent(mContext, SecondActivity.class);
(mContext).startActivity(intent);
((Activity) mContext).overridePendingTransition(R.anim.slide_in_right, R.anim.slide_out_left);
}
#Override
public void updateDrawState(TextPaint ds) {
super.updateDrawState(ds);
ds.setUnderlineText(false);
ds.setColor(ContextCompat.getColor(mContext, R.color.body_text_3));
}
};
mySpannable.setSpan(myClickableSpan, index1, index2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
Can anyone help me here, Thanks in advance.
If you are using the autolink property in the TextView then you need to set the TextView to not be focusable after setting the movement method.
To do this add the following line after postTextView.setMovementMethod(LinkMovementMethod.getInstance());:
postTextView.setFocusable(false);
An explanation is in the second paragraph from the Android setMovementMethod documentation:
Sets the MovementMethod for handling arrow key movement for this
TextView. This can be null to disallow using the arrow keys to move
the cursor or scroll the view.
Be warned that if you want a TextView with a key listener or movement
method not to be focusable, or if you want a TextView without a key
listener or movement method to be focusable, you must call
View.setFocusable(boolean) again after calling this to get the
focusability back the way you want it.
Link to the documentation:
https://developer.android.com/reference/android/widget/TextView#setMovementMethod(android.text.method.MovementMethod)
I had this exact same issue, it was because I had the "autolink: true" property in the respective view, removing it helped.
Hi I want to show/hide the content by clicking button. I am having a problem to hide the content by clicking button.
Here is my code for hiding the content
private boolean visible;
protected Button SearchButton;
private void Toggle(){
if(visible=false){
DishButton.setVisibility(View.INVISIBLE);
SpoonButton.setVisibility(View.INVISIBLE);
cupButton.setVisibility(View.INVISIBLE);
FridgeButton.setVisibility(View.INVISIBLE);
}
else {
DishButton.setVisibility(View.VISIBLE);
SpoonButton.setVisibility(View.VISIBLE);
cupButton.setVisibility(View.VISIBLE);
FridgeButton.setVisibility(View.VISIBLE);
visible=true;
}
}
if(visible=false)
is not gonna work!
Use if(visible==false).
Note that you can use View.GONE to hide the content and free the empty space.
From your comments and question it seems like
You have not put any listener to your button.
You have written = in place of ==.
You have user View.INVISILE which will permanently hide the element it will not come back. SO use View.GONE
You have some logic flaw in case of handling visible/invisible.
You have not initialized visible boolean with true because as first time you are showing all the buttons so it should be true.
So possible solution is
In your onCreate() method add
visible=true;
SearchButton.setOnclickListener(new OnClickListener()
{
public void onClick(View v)
{
Toggle();
}
});
And make the Toggle method look like
private void Toggle(){
if(visible==true){
DishButton.setVisibility(View.GONE);
SpoonButton.setVisibility(View.GONE);
cupButton.setVisibility(View.GONE);
FridgeButton.setVisibility(View.GONE);
visible=false;
}
else {
DishButton.setVisibility(View.VISIBLE);
SpoonButton.setVisibility(View.VISIBLE);
cupButton.setVisibility(View.VISIBLE);
FridgeButton.setVisibility(View.VISIBLE);
visible=true;
}
}
I am using Gabrielemariotti's Cardslib library to implement card layout in my android application. I am using a custom layout for my cards. Below is the code for creating custom cards:
Card card = new Card(getActivity().getApplicationContext(), R.layout.status_card);
card.setTitle("sample title");
I have three buttons at the bottom of my card (like buttons in Facebook android app). I want to set onClickListener for these buttons. But I am not sure how to do that.
Please help me here.
Thanks,
You have to define your layout.
Then create a Card with this layout, and override the setupInnerViewElements method.
Inside this method you can define your OnClickListener on your buttons, and you can access to all card's values.
public class CustomCard extends Card {
/**
* Constructor with a custom inner layout
*
* #param context
*/
public CustomCard(Context context) {
super(context, R.layout.carddemo_mycard_inner_content);
}
#Override
public void setupInnerViewElements(ViewGroup parent, View view) {
//Retrieve button
Button myButton = (Button) view.findViewById(R.id.myButton);
if (myButton != null) {
myButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(getContext(), "Click Listener card=" + getId(),
Toast.LENGTH_LONG).show();
}
});
}
}
}
I have an easy solution for this.
So another way to add onClick listeners, which is a bit easier, is through the XML.
In the xml for the button, you add this line:
android:onClick="methodName"
Where 'methodName' is obviously the name of a method. This will call the method whenever the button is clicked. The next step is obvious - just go into your java activity and create the method that you want called, making sure to take the View as a parameter. So you'd have something like this in your activity class:
public void methodName(View view) {
Log.v("appTag","BUTTON WAS PRESSED");
//whatever you want to do here
}
This is a shortcut to creating a whole onClickListener.
Hope that helps. Good luck :)
EDIT:
Remember, you're passing in a view here, so you can get whatever you want off of that view. Since you commented that you need to get the text off of your card, I'll show you how to do that.
Here is your method for this case:
public void methodName(View view) {
Log.v("appTag","BUTTON WAS PRESSED");
TextView textFromCard = view.findViewById(R.id.THE_ID_YOU_GAVE_YOUR_TEXTVIEW_IN_THE_XML);
String textFromTextView = textFromCard.getText().toString();
//do whatever you want with the string here
}
I am a new in android programming, I made a layout with this figure:
Now I want to know when one of these buttons clicked I should run an new activity or change visibility to false and show new layout without run a new activity, what is the best solution?
You consider that count of these buttons are more than ten.
I want show a text with image,..(when clicked) because that is a educational book and these buttons are chapters list of that book
for an example if you want to change only the layout then you could do something like this
FirstButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(final View v) {
FirstView();
}
});
/
void FirstView(){
setContentView(R.layout.yourOtherLayout);
// then declare the layout views here.
firstView=false;
}
you can do this in all the buttons just create different methods for each
to handle the Back Button you can declare Boolean variables and use If else Statement to loop through them for example
boolean firstView = true, secondView = true;
#Override
public void onBackPressed(){
if (firstView == false ){
then firstView Is Showing.
// show the view you want and set
firstView = true;
}else if (SO ON)...
else { super.OnBackPressed(); // exit }
}