I have made a linear layout on which I am adding views, simulating a table because I have read that a listview inside a scrollview is not an option :)
So I have a linear layout with wrap_content inside a scrollview.
The problem is that for the views I am adding I can't trigger any press state to change the graphics as before when it was in the listview it worked great.
I have the following:
if (arrayWorksWith.size()!=0)
{
this.listAdapter = new WorksWithListAdapter(this, R.layout.works_with_cell, R.id.lblItemTitle, arrayWorksWith);
for (int i=0; i<arrayWorksWith.size(); i++)
{
View v = this.listAdapter.getView(i, null, layoutTblWorksWith);
v.setTag(new Integer(1000 + arrayWorksWith.get(i).getId()));
v.setOnClickListener(this);
layoutTblWorksWith.addView(v);
}
}
The problem is that the click event gets fired but I don't have any visual feedback. Inside that view I have an Imageview with the background set to the following drawable:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" android:drawable="#drawable/r1" /> <!-- pressed -->
<item android:state_focused="true" android:drawable="#drawable/r1" /> <!-- focused -->
<item android:drawable="#drawable/transparent_background" /> <!-- default -->
</selector>
Also I have just tried a touch listener on the view I am adding, but this fails as well :(
v.setOnTouchListener(new OnTouchListener()
{
public boolean onTouch (View v, MotionEvent event)
{
TextView lblCategoryTitle = (TextView)v.findViewById(R.id.lblItemTitle);
if(event.getAction() == MotionEvent.ACTION_DOWN)
{
lblCategoryTitle.setTextColor(R.color.white);
}
else if (event.getAction() == MotionEvent.ACTION_UP)
{
lblCategoryTitle.setTextColor(R.color.textLabel_blue);
}
return false;
}
});
So when this code was inside a ListView when I tapped on a cell the image was changing the background giving visual feedback, but when I am creating it now and adding it to a LinearLayout it stopped working.
Any ideas? I have tried playing with focusable, clickable, duplicate parent state, but with no luck.
You have to set the listView on to an OnItemClickListener, not on an OnClickListener.
Related
On Android, a Button changes its background color when pressed.
How can we tell a button that it is pressed (without firing the onClick-action), so that it changes color, without the user pressing it? (for example triggered by a swipe action)
It should change color briefly, and then change back.
There a quite a few questions concerning keeping the pressed state. This question asks, how to set the button_pressed state briefly, as if clicked, but without a real click.
Button.setPressed(true) has not given a color change, neither has Button.performClick().
First, create the effect when button is hovered, clicked etc in XML. Put this style in your drawable.
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Pressed button -->
<item android:drawable="#color/dark_green"
android:state_focused="true"
android:state_pressed="false"
/>
<item android:drawable="#color/dark_green"
android:state_focused="true"
android:state_pressed="true"
/>
<item android:drawable="#color/dark_green"
android:state_focused="false"
android:state_pressed="true"/>
<!-- Normal button -->
<item android:drawable="#color/green"
android:state_focused="false"
android:state_pressed="false"/>
</selector>
Then in your XML, initiates the style by using:
<Button
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:background="#drawable/the_style_in_drawable"
android:text="click"/>
By putting the style in your XML, you don't have to initiate the style when button on click. Android will detect the button state and do the work for you. Just remember to put the state in selector.
To change a button state without anything else is done via
btn1.getBackground().setState(new int[]{android.R.attr.state_pressed});
To reset to ordinary, you use
btn1.getBackground().setState(new int[]{android.R.attr.state_enabled});
A Button's states can be found out via
btn1.getBackground().getState();
which resturns an int[]. You can compare its values to android.R.attr to find out which states are set.
Example Code
private void simulateClick(final ImageButton button,
final long clickDuration) {
button.getBackground().setState(new int[]{android.R.attr.state_pressed});
new Thread(new Runnable() {
public void run() {
try {
Thread.sleep(clickDuration);
} catch ( InterruptedException e ) {
// not bad if interrupted: sleeps a bit faster (can happen?)
}
Count.this.runOnUiThread(new Runnable() {
public void run() {
button.getBackground().setState(new int[]{android.R.attr.state_enabled});
}
});
}}).start();
}
Explanation
Each View has a Drawable as background image. A Drawable can be of different subtypes, here it is a StateListDrawable, as defined per XML. (See #Lynx's answer as an example of a XML defined drawable).
This Drawable can be told which state it is to assume (via setState) and does the layout itself.
AsyncTask for button color change illusion:
private class ChangeButtonColorMomentarily extends AsyncTask<String, Void, String> {
#Override
protected void onPreExecute() {
btn1.setBackgroundDrawable(new ColorDrawable(Color.rgb(50, 50, 50)));//pressed state
}
#Override
protected String doInBackground(String... params) {
try {
Thread.sleep(1000);
} catch (Exception e) {
}
return "";
}
#Override
protected void onPostExecute(String result) {
btn1.setBackgroundDrawable(new ColorDrawable(Color.rgb(200, 200, 200)));//normal state
}
}
Also take note that if your API 16 above use setBackground() instead.
For changing the color of button at that time, you can use setOnTouchListener as:
button.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_DOWN){
//Button Pressed
}
if(event.getAction() == MotionEvent.ACTION_UP){
//finger was lifted
}
return false;
}
});
I'm using the RecyclerView like below:
<android.support.v7.widget.RecyclerView
android:id="#+id/list"
android:layout_width="320dp"
android:layout_height="match_parent"/>
and my list item:
<LinearLayout android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/selector_medium_high">
<com.basf.suvinil.crie.ui.common.widget.CircleView
android:id="#+id/circle"
android:layout_width="22dp"
android:layout_height="22dp"/>
<TextView
android:id="#+id/label"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="57.5dp"/>
</LinearLayout>
see in detail this part android:background="#drawable/selector_medium_high" it's a normal selector:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#color/background_high" android:state_activated="true"/>
<item android:drawable="#color/background_high" android:state_pressed="true"/>
<item android:drawable="#color/background_high" android:state_checked="true"/>
<item android:drawable="#color/background_high" android:state_focused="true"/>
<item android:drawable="#color/background_medium"/>
</selector>
but when I run this code, i have no changes in background color when I touch the row....
Set clickable, focusable, focusableInTouchMode to true in all elements of RecyclerView "list".
Add :
android:background="?android:attr/selectableItemBackground"
in item.xml
If nothing of this works for you, like it didn't for me, use this code:
android:foreground="?android:attr/selectableItemBackground"
The trick is in android:foreground attribute...
Adding android:background="?android:attr/selectableItemBackground" to my_list_item.xml's root layout seems to work for me (assuming you want the default selection colour).
Also make sure the root layout's android:layout_width is match_parent rather than wrap_content to ensure that the whole row is selectable.
Unfortunately using focusable views to simulate item selection is not a good solution because:
Focus is lost when notifyDataSetChanged is called
Focus is problematic when child views are focusable
I wrote a base adapter class to automatically handle item selection with a RecyclerView. Just derive your adapter from it and use drawable state lists with state_selected, like you would do with a list view.
I have a Blog Post Here about it, but here is the code:
public abstract class TrackSelectionAdapter<VH extends TrackSelectionAdapter.ViewHolder> extends RecyclerView.Adapter<VH> {
// Start with first item selected
private int focusedItem = 0;
#Override
public void onAttachedToRecyclerView(final RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
// Handle key up and key down and attempt to move selection
recyclerView.setOnKeyListener(new View.OnKeyListener() {
#Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
RecyclerView.LayoutManager lm = recyclerView.getLayoutManager();
// Return false if scrolled to the bounds and allow focus to move off the list
if (event.getAction() == KeyEvent.ACTION_DOWN) {
if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
return tryMoveSelection(lm, 1);
} else if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {
return tryMoveSelection(lm, -1);
}
}
return false;
}
});
}
private boolean tryMoveSelection(RecyclerView.LayoutManager lm, int direction) {
int tryFocusItem = focusedItem + direction;
// If still within valid bounds, move the selection, notify to redraw, and scroll
if (tryFocusItem >= 0 && tryFocusItem < getItemCount()) {
notifyItemChanged(focusedItem);
focusedItem = tryFocusItem;
notifyItemChanged(focusedItem);
lm.scrollToPosition(focusedItem);
return true;
}
return false;
}
#Override
public void onBindViewHolder(VH viewHolder, int i) {
// Set selected state; use a state list drawable to style the view
viewHolder.itemView.setSelected(focusedItem == i);
}
public class ViewHolder extends RecyclerView.ViewHolder {
public ViewHolder(View itemView) {
super(itemView);
// Handle item click and set the selection
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// Redraw the old selection and the new
notifyItemChanged(focusedItem);
focusedItem = mRecyclerView.getChildPosition(v);
notifyItemChanged(focusedItem);
}
});
}
}
}
viewHolder.mRlPrince.setOnTouchListener(new View.OnTouchListener() {
#Override public boolean onTouch(View v, MotionEvent event) {
if (event.getAction()==MotionEvent.ACTION_DOWN){
viewHolder.mRlPrince.setBackgroundColor(Color.parseColor("#f8f8f8"));
}if (event.getAction()==MotionEvent.ACTION_UP){
viewHolder.mRlPrince.setBackgroundColor(Color.WHITE);
}
return false;
}
});
Add this below attribute in your Item "my_list_item.xml"
android:descendantFocusability="blocksDescendants"
This works for me !
You need to set android:clickable="true" in the element xml, and if you have more selectors in some view inside your view, you need to set android:duplicateParentState="true"
there too.
Thats works on pre honeycomb apis.
As many others answered the only way is to combine selectors and new Classes to keep track of the selection, but better to delegate this calculation to the Adapter. The library FlexibleAdapter keeps track of the selections for you, configuration change is also compatible.
Backgrounds color with ripple can now be done without XML part, but in the code to manage dynamic data.
Finally, you can use lot of features with the same library, selection is a tiny basic feature you can have.
Please have a look at the description, Wiki pages and full working example: https://github.com/davideas/FlexibleAdapter
on your design or xml file just place the following lines.
android:clickable="true"
android:focusable="true"
android:background="?android:attr/activatedBackgroundIndicator"
I have created a sliding menu.
In that i put image-view, on clicking image-view it shows me content,all works fine.
Now what i want to do is,
on selecting image-view, which is active i want to change its image and remaining things will still be unchanged... (i have created for that adapter )
private void onMenuItemClick(AdapterView<?> parent, View view,
int position, long id) {
//bla bla bla
//some other code
if (selectedItem.compareTo("ABC") == 0) {
View subview = view.findViewById(R.id.list_image);
((ImageView) subview).setImageResource(R.drawable.img1);
fragment = new abcactivity();
} else if (selectedItem.compareTo("XYZ") == 0) {
View subview = view.findViewById(R.id.list_image);
((ImageView) subview).setImageResource(R.drawable.img2);
fragment = new xyzactivity();
} else if (selectedItem.compareTo("Another") == 0) {
View subview = view.findViewById(R.id.list_image);
((ImageView) subview).setImageResource(R.drawable.img3);
fragment = new anotheractivity();
}
here i put images in
images = new ArrayList<Integer>();
images.add(R.drawable.img1);
images.add(R.drawable.img2);
images.add(R.drawable.img3);
I want change only activated image and that's also i have done but it should change to previous if i am selecting second position image.!
I think what you need is a selector file. You need add a image_change.xml in your /res/drawable folder.
img_selector.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#drawable/image_white" android:state_activated="false" />
<item android:drawable="#drawable/image_black" android:state_pressed="true" />
<item android:drawable="#drawable/image_black" android:state_activated="true" />
</selector>
And in your ImageView, you must set the drawable file:
<ImageView
...
android:background="#drawable/img_selector"
... />
Or, in your code:
images.setBackground(R.drawable.img_selector);
I hope I've helped
I'm sure this is answered somewhere, but I can't find it.
How would I go about making a textview gradually brighten/highlighted during an OnLongClickListener? I have the following code:
jObjTv1.setOnLongClickListener(new View.OnLongClickListener() {
public boolean onLongClick(View v) {
Toast.makeText(root,"Test", Toast.LENGTH_SHORT).show();
return true;
}
});
Which works just showing me the toast after the long click, but I'd like the textView to be highlighted while being "clicked".
I guess you will have to set a state list drawable as your background for your TextView
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" android:drawable="#drawable/background_transition" />
</selector>
Following is my source code,
I also tried isPressed , isClicked , but it still doesn't work.
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
// This a new view we inflate the new layout
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.my_item_layout, parent, false);
}
MyItem myItem = getItem(position);
TextView direction = (TextView) convertView.findViewById(R.id.direction);
direction.setText(myItem .getDirection());
if(convertView.isSelected()){
convertView.setBackgroundResource(R.drawable.list_select_bar);
setTextColor(convertView, textIDs , R.color.white);
}else{
convertView.setBackgroundResource(R.color.light_white);
setTextColor(convertView, textIDs , R.color.black);
}
return convertView;
}
Actually , if I remove the convertView check block, I just need to register an onItemClickListener onto the listview... but if I do this , seems makes getView method meaningless. I am so struggled with this issue.
getView is called only while rendering the list view. Its is not called when an item is selected or clicked.
Probably what u need is to use convertView.setOnFocusChangeListener
getView() creates (or re-creates) a View that wasn't visible. So a new view like this cannot be selected, pressed or clicked.
I believe you want a Color State Selector.
(From the link above)
XML file saved at res/color/button_text.xml:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true"
android:color="#ffff0000"/> <!-- pressed -->
<item android:state_focused="true"
android:color="#ff0000ff"/> <!-- focused -->
<item android:color="#ff000000"/> <!-- default -->
</selector>
This layout XML will apply the color list to a View:
<Button
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="#string/button_text"
android:textColor="#color/button_text" />
Or you can use a similar method with a ListView State Selector for ListViews, GridViews, etc.
I'm assuming you want the background color to change only when you've got your finger down on the row and change back when you let go? To do this, just add on OnTouchListener to your row item.
convertView.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
convertView.setBackgroundResource(R.drawable.list_select_bar);
} else if (event.getAction() == MotionEvent.ACTION_UP
|| event.getAction() == MotionEvent.ACTION_CANCEL) {
convertView.setBackgroundResource(R.color.light_white);
}
return false;
}
);
First , I appreciate that Sam gave me such a useful tip.
Following is my resolve procedure.
Write a selector file and below is its content. then put this list_background.xml into drawable folder
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#drawable/list_select_bar" android:state_pressed="true"/><!--pressed -->
<item android:drawable="#color/light_white"/><!-- default --></selector>
then in your item layout xml , using list_background like this:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
...
android:background="#drawable/list_background"
...
>
That's it. No click listener or other method invoked in the source code.