I'm trying to play around with some Kotlin and Anko (more familiar with iOS) and taking from their example, there is this code:
internal open class TextListWithCheckboxItem(val text: String = "") : ListItem {
protected inline fun createTextView(ui: AnkoContext<ListItemAdapter>, init: TextView.() -> Unit) = ui.apply {
textView {
id = android.R.id.text1
text = "Text list item" // default text (for the preview)
isClickable = true
setOnClickListener {
Log.d("test", "message")
}
init()
}
checkBox {
id = View.generateViewId()
setOnClickListener {
Log.d("hi", "bye")
}
init()
}
}.view
My row appears how I want with a checkbox and textview. But I want to bind an action to the row selection not the checkbox selection. Putting a log message in both, I see that I get a log message when the row is selected which flips the checkbox. It does not, however, log my "test:message" from the textView click handler. Is there a way to get around this?
Apparently your issue has been addressed here. As the checkbox is consuming all the focus of ListItem you should set the CheckBox's focusable flag to false:
checkBox {
focusable = View.NOT_FOCUSABLE
}
Unfortunately setFocusable call requires at least API 26, but you could define view .xml and inflate the view manually as described here:
<CheckBox
...
android:focusable="false" />
Alternatively you could try setting a onTouchListener returning false which means the touch event will be passed to underlying views.
Let me know if it works ;)
Related
In particular class, from many places call back is coming, i just want to whether it's coming text or button for example, so that I can set the data accordingly.
NOTE: I'm not talking about parent layout, I want to know exact name where the event click is happened!
If I'm doing this: Log.d("Hello", "Clicked finally: "+ view?.id)
This is coming:
D: Clicked finally: 2131296625
If you are using Kotlin, You can simply check if the View is Button or Image using is operator like:
when(view) {
is Button -> {
// a Button is clicked.
}
is AppCompatImageView -> {
// an Image is clicked.
}
else -> {
// any other view is clicked.
}
}
You can do something like this:
if (view.javaClass.simpleName == Button::class.java.simpleName) {
// it is a button
} else if (view.javaClass.simpleName == TextView::class.java.simpleName) {
// it is a text view
}
Please note that this won't work if you are using any subclass of Button or TextView. You will need to explicitly specify the class you want to check for.
The way app works is the following: App prompts 30 buttons to user and user may guess the right ones by tapping. When user taps some button all the buttons (say a view containing these buttons) should be locked while corresponding (right or wrong guess) animation is playing. Tapped button by itself should be disabled till the next round. After animation is finished all not tapped previously buttons (say a view containing these buttons) should be available again.
So I have a Layout which includes another layout with these 30 buttons:
...
<RelativeLayout
android:id="#+id/alphabetContainer"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<include layout="#layout/alphabet" />
</RelativeLayout>
...
Now I need to lock the buttons from being clicked and then unlock. So I tried:
...
private RelativeLayout alphabetPanel;
...
public void onCreate(){
...
alphabetPanel = (RelativeLayout) findViewById(R.id.alphabetContainer);
...
}
...
private void lockButtons(){
alphabetPanel.setEnabled(false);
}
but this doesn't lock buttons. I also tried:
alphabetPanel.setFocusable(false);
alphabetPanel.setClickable(false);
Doesn't help either. Seems like it all relies only to a layout by itself but not the views it contains.
Also I tried to add a fake layout to place it over layout with buttons by bringing it to the front. This is a workaround and its tricky cuz both layouts must be placed inside a RelativeLayout only:
...
blockingLayout = new RelativeLayout(this);
blockingLayout.setLayoutParams(alphabetPanel.getLayoutParams());
...
but this works very strange: somehow both layouts in this case appears and disappears every second or so or doesn't appear at all - I cant understand that at all cuz there is no setVisibility() method used in code!
The only one way left is to iterate every view (button) to make it disabled and than back.
Is there any other way?
UPDATE
Finally I had to add a "wall"-layout into the xml. Now by making it clickable and focusable it becomes a solution.
Try setting for each Button's xml definition
android:duplicateParentState="true"
I'm not sure, but I think it should make them not only to seem disabled, but also to act accordingly.
Hmm it surprises me that disabling the parent-layout doesn't work.. as far as i know it should.
Try fetching your included layout instead, and disable that.
Anyway, if all else fails you can always loop through the buttons themselves.
for(int i=0;i<relativeLayout.getChildCount();i++){
View child=relativeLayout.getChildAt(i);
//your processing....
child.setEnabled(false);
}
I used extension to lock and unlock the view
//lock
fun View.lock() {
isEnabled = false
isClickable = false}
//unlock
fun View.unlock() {
isEnabled = true
isClickable = true}
if you want to lock all children of the view group
//lock children of the view group
fun ViewGroup.lockAllChildren() {
views().forEach { it.lock() }}
//unlock children of the view group
fun ViewGroup.unlockAllChildren() {
views().forEach { it.unlock() }}
firstly define your button
Button bit = (Button)findViewById(R.id.but);
bit.setEnabled(false);
and set enabled false;
Java:-
public void disableButtons(Layout layout) {
// Get all touchable views
ArrayList<View> layoutButtons = layout.getTouchables();
// loop through them, if they are instances of Button, disable them.
for(View v : layoutButtons){
if( v instanceof Button ) {
((Button)v).setEnabled(false);
}
}
}
Kotlin:-
fun disableButtons(layout: Layout) {
// Get all touchable views
val layoutButtons: ArrayList<View> = layout.getTouchables()
// loop through them, if they are instances of Button, disable them.
for (v in layoutButtons) {
if (v is Button) {
(v as Button).setEnabled(false)
}
}
}
Retrieve all touchables views into an ArrayList, then loop through them and check if it is an instance of the Button or TextView or which ever you want, then disable it!
In case data binding is needed
import android.view.ViewGroup
import android.widget.Button
import androidx.core.view.children
import androidx.databinding.BindingAdapter
#BindingAdapter("disableButtons")
fun ViewGroup.setDisableButtons(disableButtons: Boolean) {
children.forEach {
(it as? Button)?.isEnabled = !disableButtons
}
}
Usage:
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="#dimen/guideline"
app:disableButtons="#{vm.busy}">
....
</androidx.constraintlayout.widget.ConstraintLayout>
Might work in constraint layout . Use group widget and add all the button ids.
In the java code set enabled false for the group.
For disable all buttons in any nested layouts.
void DisableAllButtons( ViewGroup viewGroup ){
for( int i = 0; i < viewGroup.getChildCount(); i++ ){
if( viewGroup.getChildAt(i) instanceof ViewGroup ){
DisableAllButtons( (ViewGroup) viewGroup.getChildAt(i) );
}else if( viewGroup.getChildAt(i) instanceof Button ){
viewGroup.getChildAt(i).setEnabled( false );
}
}
}
write these two lines on your button declartion in XML
android:setEnabled="false"
android:clickable="false"
I have a dynamic list of subscriber buttons, each which have several dynamically generated card type buttons that are associated with the Subscriber. In my subscriber button onClick, I would like to display (or hide) the Card Type buttons, but I'm having an issue figuring out how to associate the group of cards with the particular subscriber. Here is what I have in my Onclick so far:
View.OnClickListener getSubscriberOnClick(final IdCardSubscriberButton subscriberButton) {
return new View.OnClickListener() {
public void onClick(View v) {
Drawable icon;
if(subscriberButton.isExpanded() == false) {
icon = getResources().getDrawable(R.drawable.ic_id_card_close);
subscriberButton.setExpanded(true);
//Here's where I need to display the card Type buttons. How do I associate the
//card type buttons with this particular subscriber button?
} else {
icon = getResources().getDrawable(R.drawable.ic_id_card_dropdown);
subscriberButton.setExpanded(false);
}
subscriberButton.setCompoundDrawablesWithIntrinsicBounds(null,null,icon,null);
}
};
}
Any help would be appreciated.
Use setTag to set the same tag for buttons in same group. And use getTag to set the visibility of tag with same group.
I like my UIs to be intuitive; each screen should naturally and unobtrusively guide the user on to the next step in the app. Barring that, I strive to make things as confusing and confounding as possible.
Just kidding :-)
I've got three TableRows, each containing a read-only and non-focusable EditText control and then a button to its right. Each button starts the same activity but with a different argument. The user makes a selection there and the sub-activity finishes, populating the appropriate EditText with the user's selection.
It's the classic cascading values mechanism; each selection narrows the available options for the next selection, etc. Thus I'm disabling both controls on each of the next rows until the EditText on the current row contains a value.
I need to do one of two things, in this order of preference:
When a button is clicked, immediately remove focus without setting focus to a different button
Set focus to the first button when the activity starts
The problem manifests after the sub-activity returns; the button that was clicked retains focus.
Re: #1 above - There doesn't appear to be a removeFocus() method, or something similar
Re: #2 above - I can use requestFocus() to set focus to the button on the next row, and that works after the sub-activity returns, but for some reason it doesn't work in the parent activity's onCreate().
I need UI consistency in either direction--either no buttons have focus after the sub-activity finishes or each button receives focus depending on its place in the logic flow, including the very first (and only) active button prior to any selection.
Using clearFocus() didn't seem to be working for me either as you found (saw in comments to another answer), but what worked for me in the end was adding:
<LinearLayout
android:id="#+id/my_layout"
android:focusable="true"
android:focusableInTouchMode="true" ...>
to my very top level Layout View (a linear layout). To remove focus from all Buttons/EditTexts etc, you can then just do
LinearLayout myLayout = (LinearLayout) activity.findViewById(R.id.my_layout);
myLayout.requestFocus();
Requesting focus did nothing unless I set the view to be focusable.
Old question, but I came across it when I had a similar issue and thought I'd share what I ended up doing.
The view that gained focus was different each time so I used the very generic:
View current = getCurrentFocus();
if (current != null) current.clearFocus();
You can use View.clearFocus().
Use View.requestFocus() called from onResume().
android:descendantFocusability="beforeDescendants"
using the following in the activity with some layout options below seemed to work as desired.
getWindow().getDecorView().findViewById(android.R.id.content).clearFocus();
in connection with the following parameters on the root view.
<?xml
android:focusable="true"
android:focusableInTouchMode="true"
android:descendantFocusability="beforeDescendants" />
https://developer.android.com/reference/android/view/ViewGroup#attr_android:descendantFocusability
Answer thanks to:
https://forums.xamarin.com/discussion/1856/how-to-disable-auto-focus-on-edit-text
About windowSoftInputMode
There's yet another point of contention to be aware of. By default,
Android will automatically assign initial focus to the first EditText
or focusable control in your Activity. It naturally follows that the
InputMethod (typically the soft keyboard) will respond to the focus
event by showing itself. The windowSoftInputMode attribute in
AndroidManifest.xml, when set to stateAlwaysHidden, instructs the
keyboard to ignore this automatically-assigned initial focus.
<activity
android:name=".MyActivity"
android:windowSoftInputMode="stateAlwaysHidden"/>
great reference
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/ll_root_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
LinearLayout llRootView = findViewBindId(R.id.ll_root_view);
llRootView.clearFocus();
I use this when already finished update profile info and remove all focus from EditText in my layout
====> Update: In parent layout content my EditText add line:
android:focusableInTouchMode="true"
What about just adding android:windowSoftInputMode="stateHidden" on your activity in the manifest.
Taken from a smart man commenting on this: https://stackoverflow.com/a/2059394/956975
I tried to disable and enable focusability for view and it worked for me (focus was reset):
focusedView.setFocusable(false);
focusedView.setFocusableInTouchMode(false);
focusedView.setFocusable(true);
focusedView.setFocusableInTouchMode(true);
First of all, it will 100% work........
Create onResume() method.
Inside this onResume() find the view which is focusing again and again by findViewById().
Inside this onResume() set requestFocus() to this view.
Inside this onResume() set clearFocus to this view.
Go in xml of same layout and find that top view which you want to be focused and set focusable true and focusableInTuch true.
Inside this onResume() find the above top view by findViewById
Inside this onResume() set requestFocus() to this view at the last.
And now enjoy......
android:focusableInTouchMode="true"
android:focusable="true"
android:clickable="true"
Add them to your ViewGroup that includes your EditTextView.
It works properly to my Constraint Layout. Hope this help
You could try turning off the main Activity's ability to save its state (thus making it forget what control had text and what had focus). You will need to have some other way of remembering what your EditText's have and repopulating them onResume(). Launch your sub-Activities with startActivityForResult() and create an onActivityResult() handler in your main Activity that will update the EditText's correctly. This way you can set the proper button you want focused onResume() at the same time you repopulate the EditText's by using a myButton.post(new Runnable(){ run() { myButton.requestFocus(); } });
The View.post() method is useful for setting focus initially because that runnable will be executed after the window is created and things settle down, allowing the focus mechanism to function properly by that time. Trying to set focus during onCreate/Start/Resume() usually has issues, I've found.
Please note this is pseudo-code and non-tested, but it's a possible direction you could try.
You do not need to clear focus, just add this code where you want to focus
time_statusTV.setFocusable(true);
time_statusTV.requestFocus();
InputMethodManager imm = (InputMethodManager)this.getSystemService(Service.INPUT_METHOD_SERVICE);
imm.showSoftInput( time_statusTV, 0);
Try the following (calling clearAllEditTextFocuses();)
private final boolean clearAllEditTextFocuses() {
View v = getCurrentFocus();
if(v instanceof EditText) {
final FocusedEditTextItems list = new FocusedEditTextItems();
list.addAndClearFocus((EditText) v);
//Focus von allen EditTexten entfernen
boolean repeat = true;
do {
v = getCurrentFocus();
if(v instanceof EditText) {
if(list.containsView(v))
repeat = false;
else list.addAndClearFocus((EditText) v);
} else repeat = false;
} while(repeat);
final boolean result = !(v instanceof EditText);
//Focus wieder setzen
list.reset();
return result;
} else return false;
}
private final static class FocusedEditTextItem {
private final boolean focusable;
private final boolean focusableInTouchMode;
#NonNull
private final EditText editText;
private FocusedEditTextItem(final #NonNull EditText v) {
editText = v;
focusable = v.isFocusable();
focusableInTouchMode = v.isFocusableInTouchMode();
}
private final void clearFocus() {
if(focusable)
editText.setFocusable(false);
if(focusableInTouchMode)
editText.setFocusableInTouchMode(false);
editText.clearFocus();
}
private final void reset() {
if(focusable)
editText.setFocusable(true);
if(focusableInTouchMode)
editText.setFocusableInTouchMode(true);
}
}
private final static class FocusedEditTextItems extends ArrayList<FocusedEditTextItem> {
private final void addAndClearFocus(final #NonNull EditText v) {
final FocusedEditTextItem item = new FocusedEditTextItem(v);
add(item);
item.clearFocus();
}
private final boolean containsView(final #NonNull View v) {
boolean result = false;
for(FocusedEditTextItem item: this) {
if(item.editText == v) {
result = true;
break;
}
}
return result;
}
private final void reset() {
for(FocusedEditTextItem item: this)
item.reset();
}
}
Is there a way to allow the user to select / copy text in a TextView? I need the same functionality of EditText where you can long-press the control and get the popup options of select all / copy, but I need the control to look like a TextView.
Tried a few things like making an EditText use the editable="none" option or inputType="none", but those still retain the framed background of an EditText, which I don't want,
Thanks
------- Update ----------------------
This is 99% there, all I'd want is for the selection hilight to be visible (the orange stuff). Other than that it's good, could live with this though:
<EditText
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:editable="false"
style="?android:attr/textViewStyle"
android:textColor="#color/white"
android:textAppearance="#android:style/TextAppearance.Medium"
android:cursorVisible="false"
android:background="#null" />
I guess it's being caused because of cursorVisible="false" but without that the cursor is present even without any selection being made.
android:textIsSelectable works (at least in ICS - I haven't yet checked in earlier versions)
<TextView
android:id="#+id/deviceIdTV"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textIsSelectable="true"
android:text="" />
Text View needs to be enabled, focusable, longClickable and textIsSelectable
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=""
android:id="#+id/pwTextView"
android:enabled="true"
android:textIsSelectable="true"
android:focusable="true"
android:longClickable="true" />
I think I have a better solution.
Just call
registerForContextMenu(yourTextView);
and your TextView will be registered for receiving context menu events.
Then override onCreateContextMenu in your Activity
#Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
//user has long pressed your TextView
menu.add(0, v.getId(), 0, "text that you want to show in the context menu - I use simply Copy");
//cast the received View to TextView so that you can get its text
TextView yourTextView = (TextView) v;
//place your TextView's text in clipboard
ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
clipboard.setText(yourTextView.getText());
}
Hope this helps you and anyone else looking for a way to copy text from a TextView
textview1.setTextIsSelectable(true);
This will enable user to select and copy text on long clicking or as we do usually
Using Kotlin Programmatically (Manual Copy)
button.setTextIsSelectable(true)
Or, add a Kotlin property extension
var TextView.selectable
get() = isTextSelectable
set(value) = setTextIsSelectable(value)
Then call
textview.selectable = true
// or
if (textview.selectable) { ...
Using Kotlin Programmatically (Auto-Copy)
If you want to auto-copy when user long-presses you view, this is the base code required:
myView.setOnLongClickListener {
val clipboardManager = context.getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
val clip = ClipData.newPlainText("Copied String", myString)
clipboardManager.setPrimaryClip(clip)
true // Or false if not consumed
}
You may want to add a Toast to confirm it happened
Or, add a Kotlin extension function
myView.copyOnHold() // pass custom string to not use view contents
fun TextView.copyOnHold(customText: String? = null) {
setOnLongClickListener {
val clipboardManager = context.getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
val clip = ClipData.newPlainText("Copied String", customText ?: text)
clipboardManager.setPrimaryClip(clip)
true // Or false if not consumed
}
}
Using Xml (Manual Copy)
Add this to your <TextView>
android:textIsSelectable="true"
NOTE: All of these require android:enabled="true" and android:focusable="true", which are the default values for a TextView.
I'm trying to implement the same, and your question helped me to set my editext layout correctly. So Thanks! :)
Then I realized, that the highlight will actually be visible if the cursor is on.
But I just like you do not want to see a cursor before long clicking on the text, so I hide the cursor in the layout.xml file just like you, and added an eventlistener for long click and display the cursor only when a selection starts.
So add the listener in your Activity in the onCreate section:
public TextView htmltextview;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
...
htmltextview.setOnLongClickListener(new OnLongClickListener(){
public boolean onLongClick(View v) {
htmltextview.setCursorVisible(true);
return false;
}
});
}
And voilá, no cursor at the beginning, and if you long-click, the cursor appears with the selection boundaries.
I hope I could help.
Cheers,
fm
I was also trying to do something similar but still needed a custom approach with manipulation of highlighting of text in TextView. I triggered highlight and copying on LongClick action.
This is how I managed using SpannableString:
SpannableString highlightString = new SpannableString(textView.getText());
highlightString.setSpan(new BackgroundColorSpan(ContextCompat.getColor(getActivity(), R.color.gray))
, 0, textView.getText().length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
textView.setText(highlightString);
copyToClipboard(urlToShare);
and the copy function:
public void copyToClipboard(String copyText) {
ClipboardManager clipboard = (ClipboardManager) getActivity().getSystemService(Context.CLIPBOARD_SERVICE);
ClipData clip = ClipData.newPlainText("url", copyText);
clipboard.setPrimaryClip(clip);
Toast toast = Toast.makeText(getActivity(), "Link is copied", Toast.LENGTH_SHORT);
toast.show();
}
I hope it's helpful for someone who ends up on this question :)
I have found it doesn't work the first time I double click, but it works there after ( at least in android 11). This told me it needed to get focus. So, in the onCreate event, I first made the text view selectable, then I requested the focus to shift to the text view. Now I'm not saying the text view can lose focus and the first attempted selection will work. Not guaranteed. What is guaranteed is once it has focus, it'll work every time until it loses focus again. Don't forget about androids animations. So allow at least a half second for the non overridable animation to play out when the keyboard is hiding.
// In onCreate
TextView1.setTextIsSelectable( true );
// Allow animations to play out.
timer = new TimerTask() {
#Override
public void run() {
runOnUiThread(new Runnable() {
#Override
public void run() {
TextView1.requestFocus();
}
});
}
};
_timer.schedule(timer, (int)(1000));
}
Thanks a lot gilbot for your explanation. I just want to add another thing.
Selected text background color follows your app theme's colorAccent
For example check the image below
Here AppTheme is my application theme.
<item name="colorAccent">#color/cold</item>
and the colorAccent value will be the selected text background color.
Just use this simple library:
GitHub: Selectable TextView