I have a list of items that is composed of a View and a TextView. I defined an animation:
View mView = findViewById(R.id.view);
Intent detailIntent = new Intent(this, DetailActivity.class);
String transitionName = getString(R.string.my_transition);
ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(this,
mView, transitionName);
startActivity(detailIntent, options.toBundle());
and in my list_item.xml I have:
<View
android:id="#+id/view"
android:layout_width="#dimen/list_dimen"
android:layout_height="match_parent"
android:layout_marginStart="0dp"
android:background="#color/accent"
android:transitionName="#string/my_transition"/>
The transition works fine for the first item, but no matter which item in the list I click, the animation always comes from the first item, and if I scroll to the point where the first item is out of view the app force closes when I click on an item. How do I attach to animation to a specific list item?
SOLUTION:
Per MarcaoAS answer, I had to attach the view to the specific root view. I am implementing a Master-Detail relationship so I have a Fragment for my list which contains a Callbacks :
public interface Callbacks {
public void onItemSelected(String id);
}
I had to edit this to take a View as a parameter instead of a String: onItemSelected(View view).
Likewise, I had to make this change where ever that is referenced. When the Fragment was generated (I selected Master-Detail when creating the project in Android Studio) the onListItemClick was overridden:
#Override
public void onListItemClick(ListView listView, View view, int position, long id) {
super.onListItemClick(listView, view, position, id);
mCallbacks.onItemSelected(view);
}
As you can see I once again replaced the id with the view that is passed in.
In my ListActivity where I have my original code posted above I now have:
#Override
public void onItemSelected(View view) {
//tablet
if (mTwoPane) {
//tablet handling
}
//phone
else {
View mView = view.findViewById(R.id.view);
Intent detailIntent = new Intent(this, DetailActivity.class);
String transitionName = getString(R.string.my_transition);
ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(this,
mView, transitionName);
startActivity(detailIntent, options.toBundle());
}
}
Once again passing in the View from the Fragment class. This solved my issue.
Make sure that your the code below is returning the item view.
View mView = findViewById(R.id.view);
If you are calling this from an activity it will always return the first item because your items may have the same id. Try to do that in your onClick method:
public void onClick(View v) {
View mView = v.findViewById(R.id.view);
Intent detailIntent = new Intent(this, DetailActivity.class);
String transitionName = getString(R.string.my_transition);
ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(this, mView, transitionName);
startActivity(detailIntent, options.toBundle());
}
Related
I have a RecyclerView with different Views in each item: different number of views, different type and different positions. They come from a database.
Item 1
View type A,
View type B,
View type E
Item 2
View type B,
View type B,
View type J,
View type C
.
.
.
Item n
View type F,
View type S
A is for example a TextView, B for example a CheckBox ...
In the Holder constructor I get the Layout of the item:
public ViewHolderXXX(final View itemView) {
super(itemView);
mainLayout = itemView.findViewById(R.id.ly_main_layout);
}
Then, in the bindViews (), depending on the type of the view, I add another layout to the mainLayout and add the corresponding logic.
if(customObject == TextView){
mainLayout.addView(layoutForTextView);
}
if(customObject == CheckBox){
mainLayout.addView(layoutForCheckBox);
}
.
.
.
TextView textView = layoutForTextView.findViewById(R.id.tv_text_view);
textView.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View view) {
final Intent intent = new Intent(context, OtherActivity.class);
context.startActivity(intent);
}
}
Also, in the Holder I have a static class that is called from OtherActivity.
public static void notifyXXX(){
//do something
}
In OtherActivity:
ViewHolderXXX.notifyXXX();
finish();
When I return to the Holder the items do "weird things". In the last item there are layouts that I have not added. I can not find the pattern with which they appear.
I'm pretty confused. I do not know if I designed Adapter and the ViewHolder well or if a RecyclerView is not suitable for this particular task. Also, I have had to solve other quite complicated problems.
The solution in my case was to combine all the three array list into one from type Object and instantiates with the adapter constructor and then check the current position using an instance with the corresponding model.
#Override
public int getItemViewType(int position) {
//Gallery.Photo and Gallery.Vedio are the models
if (objects.get(position) instanceof Gallery.Photo) {
return VIEW_TYPE_PHOTO;
} else if (objects.get(position) instanceof Gallery.Vedio) {
return VIEW_TYPE_VIDEO;
} else {
return VIEW_TYPE_ESSAY;
}
}
My app has quite a few separate activity/fragment pairs, and relies on the Android universal back button for much of its navigation. This button works fine, EXCEPT when I'm trying to return from a DetailView activity back to a list of search results.
Here's what the search results code looks like:
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle saveInstanceState) {
View v = inflater.inflate(R.layout.results_fragment, container, false);
ListView lv;
lv = (ListView)v.findViewById(R.id.listViewResults);
lv.setAdapter(SearchResultsAdapter);
lv.setEmptyView(v.findViewById(R.id.emptyElement));
lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapter, View view, int position, long id) {
ItemType selectedItem;
selectedItem = (ItemType)adapter.getItemAtPosition(position);
Intent i = new Intent(getActivity(), DetailViewActivity.class);
i.putExtra(DetailViewFragment.RESULT_ID, resultIdNumber);
startActivity(i);
}
});
// ... some other stuff
return v;
}
The DetailView is simply a collection of images and text.
The search returns expected results, and selecting the item shows the correct DetailViewFragment.
It seems like a very typical architecture, so I'm not sure why navigation back to the results page should be so problematic. I tried setting breakpoints to determine if the results activity ever restarted, but apparently it did not.
If you want to make something when the back button is pressed, you have to override it:
#Override
public void onBackPressed()
{
// code here
finish(); // to end activity:
}
I am making an application, and I want to change an image inside some layout from another layout. Is that possible? Here is the case, the details activity contains spinner which has the items important and draft, and there is a save button which saves data in db. The main activity has a list view, each row consists of image view and text view. The xml file of the details activity is not the same as the xml file of the row layout which contains the image view. The image in the image view should change based on the value of the spinner in the saved note. I understand that the value of spinner can be passed by intents, and based on that we can set the image. But I am getting error due to referencing the id of the image view which is not in the xml file of the main activity. I am using the row xml only as argument in the loader, which is in the main activity, where the main activity xml file is different from the row xml file. In other words, there is no activity that uses row xml file as its layout, it is only used as a parameter in the loader. So, is changing the image possible in this case?
Here is the code I used in the details fragment in order to send the spinner value to a fragment in another activity:
if (isUpdateQuery) {
long id = getActivity().getIntent().getLongExtra("id", 0);
int updated = getActivity().getContentResolver().update(NotesContract.NotesTable.buildUriWithId(id), values, null, null);
Intent intent = new Intent(getActivity(), NotesList.class);
intent.putExtra("category", category);
startActivity(intent);
}//en inner if
And, this is the code I used in the fragment to get the spinner value and update the image:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View temp = inflater.inflate(R.layout.notes_row, container, false);
ImageView imageView = (ImageView) temp.findViewById(R.id.icon);
if (getActivity().getIntent().getStringExtra("category")!=null && getActivity().getIntent().getStringExtra("category").equalsIgnoreCase("important")){
imageView.setImageResource(R.drawable.staricon);
}
mSimpleCursorAdapter=new SimpleCursorAdapter(getActivity(),R.layout.notes_row,null, from, to,0);
View rootView = inflater.inflate(R.layout.fragment_todo_list, container, false);
getLoaderManager().initLoader(LOADER_ID, null, this); //once this is done onCreateLoader will be called.
final ListView listView = (ListView) rootView.findViewById(R.id.notes_list); //findViewById must be called using the rootView because we are inside a fragment.
if(mSimpleCursorAdapter.getCount()==0) {
TextView text= (TextView) rootView.findViewById(R.id.empty_list);
text.setVisibility(View.VISIBLE);
}
if (getActivity().findViewById (R.id.fragment_container)!=null){
listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);}//end if.
listView.setAdapter(mSimpleCursorAdapter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int position, long l) {
Cursor cursor = mSimpleCursorAdapter.getCursor();
if (cursor != null && cursor.moveToPosition(position)) {
String category= cursor.getString(1);
String summary= cursor.getString(2);
String description=cursor.getString(3);
long id= cursor.getLong(cursor.getColumnIndex(NotesContract.NotesTable._ID));
int locationId= cursor.getInt(cursor.getColumnIndex(NotesContract.NotesTable.COLUMN_LOCATION));
String [] retrievedData= {category, summary, description};
if (getActivity().findViewById (R.id.fragment_container)!=null){
//two pane layout:
listView.setItemChecked(position, true);
listView.setBackgroundColor(Color.BLUE);
Bundle args = new Bundle();
args.putStringArray("data",retrievedData);
/*args.putInt("update", 1);*/
args.putLong("id", id);
args.putInt("locationId", locationId);
mCallback.onlistElementClicked(args );/*this is available in the parent activity*/
}
else {
// one pane layout:
Intent intent = new Intent(getActivity(), NotesDetails.class);
intent.putExtra(Intent.EXTRA_TEXT, retrievedData);
/*intent.putExtra("update", 1); */ //to indicate that the query should be update not insert.
intent.putExtra("id", id);
intent.putExtra("locationId", locationId); //whether it is 0 or 1
startActivity(intent);
}
}//end outer cursor if.
}
});
return rootView;
}
I am getting a NullPointerException when trying to find the image by ID. As can be seen in the second code section, I have first temporarily inflate the view that contains the image in order to be able to get the image by id and update it, then I inflate the original layout of the fragment (rootView), and I returned rootView. Is that correct?
Notes: The activity that I sent the intent to has a different layout from row layout, and the fragment inside that activity has its on layout also.
Any help is appreciated.
Thank you
If you want to use the different xml other than linked to the activity, than you need to inflate the other xml in this activity. Use the code below as per your code:
public View getView(int position, View convertView, ViewGroup parent) {
View view;
view = Inflater.inflate(R.layout.another_xml, parent, false);
/* Get the widget with id name which is defined in the another_xml of the row */
ImageView imageView = (ImageView) view.findViewById(R.id.imageView);
/* Populate the another_xml with info from the item */
name.setText(myObject.getName());
/* Return the generated view */
return view;
}
Hope this helps you and I do hope that I have understood your question properly.
I have an image inside my group_row
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:background="#8fbfff" >
<TextView
android:id="#+id/tv_category_name"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:text="TextView"
android:layout_weight="1" />
<ImageView
android:id="#+id/img_add_category"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/ico_add_item"
android:layout_weight="1"
android:layout_gravity="center_vertical"
android:layout_marginRight="10dp" />
</LinearLayout>
I want to detect when this image was clicked and the id of the Group, and then StartActivityForResult. I was able to do it inside my custom ExpandableListView_Adapter:
#Override
public View getGroupView(int groupPosition, boolean isExpanded,
View convertView, ViewGroup parent) {
final Category category = arr_categories.get(groupPosition);
convertView = inflater.inflate(R.layout.row_category, parent, false);
((TextView)convertView.findViewById(R.id.tv_category_name)).setText(category.getCategory_name());
ImageView img_add = (ImageView) convertView.findViewById(R.id.img_add_category);
img_add.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(context, "Add item to category: "+category.getCategory_id(), Toast.LENGTH_LONG).show();
// Declare on intent to use Add/Edit Note activity
Intent open_add_new_item = new Intent(context, Activity_Add_Edit_Base_Item.class);
// Pass the currently selected category ID to the Intent
open_add_new_item.putExtra("CURR_ITEM_CATEGORY", category.getCategory_id());
// Start the activity
((Activity) context).startActivityForResult(open_add_new_item, Constants.Request_Codes.REQUEST_CODE_CREATE_NEW_ITEM);
}
});
return convertView;
}
This works just fine. However, If there's a possibility to do so, I'd like to separate this image click detection from the Adapter and do it in my fragment (I think it'll be easier to implement OnActivityResult this way). I tried to do it by setting OnGroupClickListener for my ExpandableListView:
master_lv.setOnGroupClickListener(new OnGroupClickListener() {
#Override
public boolean onGroupClick(ExpandableListView parent, View v,
int groupPosition, long id) {
final Category c = arr_all_categories.get(groupPosition);
ImageView img_add = (ImageView) v.findViewById(R.id.img_add_category);
img_add.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
//Category c = arr_all_categories.get(groupPosition);
Toast.makeText(getActivity(), "group position: "+c.getCategory_id(), Toast.LENGTH_LONG).show();
// Declare on intent to use Add/Edit Note activity
Intent open_add_new_item = new Intent(getActivity(), Activity_Add_Edit_Base_Item.class);
// Pass the currently selected category ID to the Intent
open_add_new_item.putExtra("CURR_ITEM_CATEGORY", c.getCategory_id());
// Start the activity
startActivityForResult(open_add_new_item, Constants.Request_Codes.REQUEST_CODE_CREATE_NEW_ITEM);
}
});
return false;
}
});
However this didn't work at all: I'm not getting the Toast message and the Intent doesn't fire.
Is it possible to do so? If it is - how? Thanks!
The first one you need to do is set clickable=true to root layout of the custom cell xml.
After that, what we are going to do is Custom Event Raising. We will use interfaces.
Create a interface class
Example :
public interface OnImageClickListener {
public void onImageClicked();
}
Then create a instance in adapter
public OnImageClickListener mListener;
Also set OnClickListener to imageview in getView method of the adapter and add the following line in OnClick method.
mListener.OnImageClicked();
Lastly, in Activity;
mAdapter.mListener = new OnImageClickListener();
Magic will happen here :)
or you can implement this interface like
public MyActivity implements OnItemClickListener and let the implement methods.
Then you can
mAdapter.mListener = this;
Good luck there :)
I have an ExpandableListView which has a lot of children and for every child I would start an activity. Someone told me to create an unique class with all the activities of every children. Is it possible? How? In my project I have a Child.class, a Group.class, a MyExpandableListView.class, a XmlHandler.class and the main activity. I take the name for the ExpandableList from xml files that are in res/raw folder. I hope someone could help me. Thank you.
If your activities triggered by the ExpandableListView's child click event are similar (I mean only the data they display is different), it's enough for you to have a single activity, and make it's content dynamic based on the selected (clicked) child of the ExpandableListView.
Let's call your new activity Details.
If your main activity doesn't extend ExpandableListActivity, but you have inside a member of type MyExpandableListView myExpandableListView, you should set the `OnChildClickListener on that:
final ExpandableListView myExpandableListView = getExpandableListView();
myExpandableListView.setOnChildClickListener(new OnChildClickListener()
{
#Override
public boolean onChildClick(ExpandableListView parent, View v,
int groupPosition, int childPosition, long id)
{
final Child selectedChild = groups.get(groupPosition)
.getChildren().get(childPosition);
final Intent intent = new Intent(testactivity.this, Details.class);
intent.putExtra("selectedChild", selectedChild);
startActivity(intent);
return true;
}
});
If your main activity extends ExpandableListActivity, to call the Detail activity, you need to override the onChildClick event of your ExpandableListActivity:
#Override
public boolean onChildClick(ExpandableListView parent, View v,
int groupPosition, int childPosition, long id)
{
final Child selectedChild = groups.get(groupPosition)
.getChildren().get(childPosition);
final Intent intent = new Intent(this, Details.class);
intent.putExtra("selectedChild", selectedChild);
startActivity(intent);
return true;
}
You add the clicked child's value as an extra to the Details activity's intent by the putExtra method, and then just start the activity.
Inside the Details activity you can retrieve the passed Child (the clicked item on your exp.listactivity) from the activity's Intent using the getSerializableExtra method (for this to work, your Child class must implement java.io.Serializable!).
Details.java:
public class Details extends Activity
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.details);
final Intent intent = getIntent();
if (intent.hasExtra("selectedChild"))
{
final Child selectedChild = (Child)intent.
getSerializableExtra("selectedChild");
if (selectedChild != null)
{
((TextView)findViewById(R.id.nameText)).
setText(selectedChild.getName());
((ImageView)findViewById(R.id.image)).setImageResource(getResources().
getIdentifier(selectedChild.getImage(), "drawable", "com.test.com"));
}
}
}
}
Your Details activity's layout should contain a text and an image
details.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" android:layout_height="100dip"
android:orientation="vertical">
<TextView android:id="#+id/nameText"
android:layout_width="fill_parent" android:layout_height="wrap_content"
android:paddingLeft="10dip" android:textColor="#android:color/white"
android:textSize="30dip" android:gravity="center_vertical|center_horizontal" />
<ImageView android:id="#+id/image"
android:layout_width="fill_parent" android:layout_height="fill_parent"
android:scaleType="fitCenter" android:layout_below="#id/nameText" />
</RelativeLayout>
By this you can achieve, that when you click on a child item of your ExpandableListActivity, you start a new activity (Details) where the selected Child's image and text are displayed.
If you put more information inside your Child class (or url from where to fetch more data), you can get it more complicated.