Reposting question: As i didn't get an answer for 15 days and i don't have enough bounty to shelve off.
I am a beginner to android programming and am trying to create an app which plays a certain sound.when the ListView item is clicked on, however when I declare the MediaPlayer object (as given below) inside the onItemClick method the app crashes.
phrasesActivityListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Word currentWord = phrases.get(position);
MediaPlayer mediaPlayer = MediaPlayer.create(PhrasesActivity.this, currentWord.getAudioResourceId());
mediaPlayer.start();
}
});
where the phrasesActivityListView is the name of the ListView
whereas when i declare a the MediaPlayer object as private the app works(as shown below )
private MediaPlayer mMediaPlayer;
.
.
phrasesActivityListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Word currentWord = phrases.get(position);
mMediaPlayer = MediaPlayer.create(PhrasesActivity.this, currentWord.getAudioResourceId());
mMediaPlayer.start();
}
});
why? And I have also included the Word Class below
package com.example.android.miwok;
public class Word {
//Default translation of the word
private String mDefaultTranslation;
//Miwok translation of the word
private String mMiwokTranslation;
//Resource id of the image to be shown on the screen.It is set to minus one so that it can be verified that whether the resource id is associated with any ImageView.
private int mImageResourceId = -1;
//Resource id of the audio file that is going to be played when the user click on the ListView item.
private int mAudioResourceId;
//Constructor to intialise the values of text in the textViewx
public Word(String defaultTranslation, String miwokTranslation, int audioResourceId) {
mDefaultTranslation = defaultTranslation;
mMiwokTranslation = miwokTranslation;
mAudioResourceId = audioResourceId;
}
//Constructor to intialise the values of text and the resource for the TextView and the ImageView.
public Word(String defaultTranslation, String miwokTranslation, int imageResourceId, int audioResourceId) {
mDefaultTranslation = defaultTranslation;
mMiwokTranslation = miwokTranslation;
mImageResourceId = imageResourceId;
mImageResourceId = audioResourceId;
}
//Returns the defaultTranslation of the word
public String getDefaultTranslation() {
return mDefaultTranslation;
}
//Returns the Miwok translation of the word.
public String getMiwokTranslation() {
return mMiwokTranslation;
}
//Returns the image resource id of the image.
public int getImageResourceId() {
return mImageResourceId;
}
//Checks whether the resource view has been intialised when creating a ImageView.
public boolean hasImage() {
return mImageResourceId != -1;
}
//Returns the audio resource id of the audio.
public int getAudioResourceId() {
return mAudioResourceId;
}
}
In your first code snippet, mediaPlayer is a local variable. It goes out of scope as soon as onItemClick() returns, and it will be eligible for garbage collection. Since you want that object to live for a bit longer than that, you need to hold onto it somewhere else, such as inside the activity or fragment in which this code resides.
The way you are preserving the object of Your list view phrasesActivityListView, in the same way You need to declare Your media player object. So that it can live longer to play whatever You wants to play.
More You should read the lifecycle of media player also. It will make clear how You should deal with media player object like release the object, sequence of prepare and start.
Related
I have the following code for the recyclerview adapter for an android app that I'm working on right now:
#Override
public void onBindViewHolder(final FeedViewHolder contactViewHolder, final int i) {
final FeedInfo ci = feedInfoList.get(i);
//Set the text of the feed with your data
contactViewHolder.feedText.setText(ci.getFeed());
contactViewHolder.surNameText.setText(ci.getSurName());
contactViewHolder.nameText.setText(ci.getFirstName());
contactViewHolder.feedDate.setText(ci.getDate());
contactViewHolder.numberOfGoingText.setText(ci.getNumber_of_going());
contactViewHolder.numberOfInterestedText.setText(ci.getNumber_of_interested());
//seteaza fotografia de profil in postare
new ProfilePictureDownloadImage(contactViewHolder.profilePicture).execute(ci.getProfileImageURL());
ImageButton interestedButton = contactViewHolder.interestedButton;
interestedButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int position = i;
FeedInfo fi = feedInfoList.get(position);
int displayedNumberOfInterested = Integer.parseInt(ci.getNumber_of_interested()) + 1;
contactViewHolder.numberOfInterestedText.setText(Integer.toString(displayedNumberOfInterested));
System.out.println("emilutzy interested from within" + fi.getPostID());
contactViewHolder.surNameText.setText("kk");
}
});
}
The problem is the click listener. In theory the button I press should increment the number right next to it. However, since I have to declare onBindViewHolder's arguments as final, only the first click works, the rest of the clicks do not change the value of the number. I am new to Android, so could you please help me find a better solution?
There's a nice method called getAdapterPosition() that you can use in your RecyclerView's ViewHolder.
Instead of setting the click listener in onBindViewHolder, set it in the constructor of your ViewHolder like so:
public class FeedViewHolder extends RecyclerView.ViewHolder {
private TextView feedText;
private TextView surNameText;
private Button interestedButton;
// ... the rest of your viewholder elements
public FeedViewHolder(View itemView) {
super(itemView);
feedtext = itemView.findViewById(R.id.feedtext);
// ... find your other views
interestedButton.setOnClickListener(new View.OnClickListener() {
final FeedInfo fi = feedInfoList.get(getAdapterPosition());
int numInterested = Integer.parseInt(ci.getNumber_of_interested()) + 1;
// setting the views here might work,
// but you will find that they reset themselves
// after you scroll up and down (views get recycled).
// find a way to update feedInfoList,
// I like to use EventBus to send an event to the
// host activity/fragment like so:
EventBus.getDefault().post(
new UpdateFeedInfoListEvent(getAdapterPosition(), numInterested));
// in your host activity/fragment,
// update the list and call
// notifyDatasetChanged/notifyDataUpdated()
//on this RecyclerView adapter accordingly
});
}
}
Don't set your position in onBindViewHolder to final (Android Studio will warn you why).
I'm not sure how the object FeedInfo looks like but you could also at a method called for example increaseNumberOfInterested() which would increase the value of Number_of_interested by one and would persist in the object when the recyclerview recycle the cell. it would like kind of like below
#Override
public void onBindViewHolder(final FeedViewHolder contactViewHolder, final int i) {
final FeedInfo ci = feedInfoList.get(i);
//Set the text of the feed with your data
contactViewHolder.numberOfInterestedText.setText(ci.getNumber_of_interested());
contactViewHolder.interestedButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//Increase the number of interested in the object, so it can be persisted when cell is reclycled
ci.setNumberOfInterested(ci.getNumber_of_interested()) + 1);
//Get new value and display
contactViewHolder.numberOfInterestedText.setText(Integer.toString(ci.getNumber_of_interested()));
}
I am facing a Firebase RecyclerView problem where I cannot remove unwanted CardViews from my RecyclerViews. In my code I check the city's name and the guide's chosen city to match them. It populates guide's details only if the guide's city matches the picked city, but it also shows empty cardview with default layout.
guideDataRef = FirebaseDatabase.getInstance().getReference().child("Guides");
public void recycler() {
super.onStart();
try {
//Guide RecyclerView
Query guideQuery = guideDataRef.orderByKey();
guideQuery.keepSynced(true);
FirebaseRecyclerOptions guideOptions =
new FirebaseRecyclerOptions.Builder<UserModelClass>().setQuery(guideQuery, UserModelClass.class).build();
guideAdapter = new FirebaseRecyclerAdapter<UserModelClass, guideViewHolder>(guideOptions) {
#Override
protected void onBindViewHolder(#NonNull guideViewHolder holder, final int position, #NonNull final UserModelClass model) {
String pickedcity = model.getPickedCity();
String postname = (String) cityName.getText();
if(pickedcity.equals(postname)) {
final String guide_key= getRef(position).getKey();
holder.setGuideName(model.getName());
holder.setGuideSurname(model.getSurName());
holder.setGuideImage(getApplicationContext(), model.getPhotoURL());
// holder.mView.setVisibility(View.VISIBLE);
//Guide Click listener
holder.mView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent guideHireIntent = new Intent(getApplication(), GuideHireActivity.class);
guideHireIntent.putExtra("guide_id", guide_key);
finish();
startActivity(guideHireIntent);
}
});
}
}
#NonNull
#Override
public guideViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_layout_guides, parent, false);
return new guideViewHolder(view);
}
#Override
public void onError(DatabaseError e){
Toast.makeText(getApplicationContext(), "Error by stopping ", Toast.LENGTH_SHORT).show();
}
#Override
public int getItemCount() {
return super.getItemCount();
}
#Override
public void onDataChanged() {
super.onDataChanged();
notifyDataSetChanged();
}
};
guideAdapter.notifyDataSetChanged();
guideRecyclerView.setAdapter(guideAdapter);
guideAdapter.startListening();
} catch (DatabaseException e) {
Toast.makeText(this, "Error", Toast.LENGTH_SHORT).show();
}
}
enter image description here
enter image description here
I can change the adapter visibility to gone if it does not match with the requirements but the problem is that after making it's visibility gone it is still there holding the place (but invisible - there's still an empty space). How can I avoid populating an item from the recycler view completely, instead of making it invisible if the requirements do not match?
You're not showing what guideDataRef is in your code, so I'm assuming that it's just aDatabaseReference object for everything beneath a \Guides node.
If you're doing that, you're going to get a call for onBindViewHolder for every child at that particular location. This means that you're going to be asked to make a view for every child. You cannot choose whether or not a view will appear for that item.
It looks like you're assuming that your if statement in onBindViewHolder method will skip over those items. But what's actually happening is that you're simply allowing an empty view to occupy that spot in the list.
Instead, you should come up with a query that generates only the items of interest to your list. This means you'll have to tell Firebase to filter for children that meet your criteria.
You can also read the entire contents of the location, manually filter out the items you don't want, and build a list of items you do want. You can then build an custom adapter with that list, and it can then become the input to a ListView or even better to a RecyclerView.
I am making use of recycler view. I have a layout that is highlighted in light red,this layout is included for each item in the recycler view. The light red layout is placed over the background image. I am using setTag method to identify the clicks of the buttons in red layout. That is working properly when i click i get the position. The problem is i want to change the image at specific position.
For example : Consider the heart button. I have set a tag on it like this.
heartButton = findViewById(id);
heartButton.setTag(position);
now i get the position by using the getTag method. But now i want to change the image of the heartButton at the a specific position. Is there something like
heartButton.getTag(position).setImageResouce(drawable);
If not how do i do this then.
use setBackgroundResource(R.drawable.XXX)
http://developer.android.com/reference/android/view/View.html#setBackgroundResource(int)
Proper way to do this is,
You have to keep the state of the heart button stored in the model(POJO) which is passed to custom adapter.
e.g.
class ModelListItem{
public static final int HEART=1,BROKEN_HEART=2;
int heartButtonState;
}
Now in onClick() of heart button, get that object from adapter using position,cosidering you have already figured it out on how to preserve position from heart button
ModelListItem item = (ModelListItem)adapter.getItem(position)
Change the state of heart button;
item.setHeartButtonState(ModelListItem.BROKEN_HEART);
adapter.notifyDatasetChanged();
You already know below explaination but just in case
To work this properly,in your getView methode of adapter you need to put the check on heartButtonState(); and use appropriate image resource.
getView(BOILERPLATE){
BOILERPLATE
switch(item.getheartButtonState()){
case ModelItemList.HEART:
heartbutton.setImageResource(heart_image);
break;
case ModelItemList.BROKEN_HEART:
heartbutton.setImageResource(broken_heart_image);
break;
}
I made a custom click listener and updated the like in the setter getter.But this works only when the view has been moved out of the view (i think it is the scrapeview)
The Setter Getter Class
public class DemoData {
int background;
boolean liked;
public DemoData(int background) {
this.background = background;
}
public int getBackground() {
return background;
}
// public void setBackground(int background) {
// this.background = background;
// }
public boolean isLiked() {
return liked;
}
public void setLiked(boolean liked) {
this.liked = liked;
}
}
The onBindViewHolder function of the recycler view
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
background = (ImageView) holder.view.findViewById(R.id.image);
layout = (LinearLayout) holder.view.findViewById(R.id.layout);
delete = (ImageView) layout.findViewById(R.id.delete);
lock = (ImageView) layout.findViewById(R.id.lock);
delete.setTag("delete_"+position);
lock.setTag("lock_"+position);
if(Constants.demoDatas.get(position).isLiked()){
delete.setImageResource(R.drawable.ic_launcher);
}
else{
delete.setImageResource(android.R.drawable.ic_delete);
}
delete.setOnClickListener(new CustomClickListener(position));
lock.setOnClickListener(new CustomClickListener(position));
}
The custom click listener is as below
public class CustomClickListener implements View.OnClickListener {
int position;
public CustomClickListener(int position) {
this.position = position;
}
#Override
public void onClick(View v) {
String tag = (String) v.getTag();
String identifier[] = tag.split("_");
// this line saves my state in the Setter Getter Class
Constants.demoDatas.get(position).setLiked(true);
}
}
This question already has an answer here:
How can I get Resources by name (string) and not by integer
(1 answer)
Closed 9 years ago.
Ok, here is what I have:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
title = (TextView) findViewById(R.id.title);
description = (TextView) findViewById(R.id.description);
Spinner dropdown = (Spinner) findViewById(R.id.mainMenu);
final String options[] = {"-Turf Diseases-", "Dollar Spot", "Red Thread"};
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item,options);
dropdown.setAdapter(adapter);
dropdown.setOnItemSelectedListener(new OnItemSelectedListener() {
public void onItemSelected(AdapterView<?> parent, View v, int position, long id) {
newSelection(options[position]);
}
public void onNothingSelected(AdapterView<?> arg0) {}
});
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
public void newSelection(String selection) {
if(!selection.contains("-")) {
title.setText(selection);
selection=selection.replace(" ", "_");
selection=selection.toUpperCase();
description.setText("#string/DESC_"+selection);
}
}
The string array of options[] holds titles of strains of lawn diseases (the purpose of the app). It is in a spinner list in the main activity, and when a user clicks a title the action listener calls this last method, newSelection. This method is supposed to format the title to: WORD_WORD.
I have the descriptions saved as predefined strings in strings.xml, all starting with DESC_WORD_WORD. So by my logic, I could do this: description.setText("#string/DESC_"+selection); and it would easily locate the corresponding description in strings.xml.
This is, in fact, what has not ended up happening. The text just changes to the "#string/DESC_WORD_WORD" instead of the predefined string. I'm trying to think like an object-oriented programmer, but it isn't working out for me... I am fairly new to android, so go easy on me if this is a dumb question.
You need to get your resource by its string ID which is done thusly...
#SuppressWarnings("rawtypes")
public static int getResourceId(String name, Class resType){
try {
Class res = null;
if(resType == R.drawable.class)
res = R.drawable.class;
if(resType == R.id.class)
res = R.id.class;
if(resType == R.string.class)
res = R.string.class;
Field field = res.getField(name);
int retId = field.getInt(null);
return retId;
}
catch (Exception e) {
// Log.d(TAG, "Failure to get drawable id.", e);
}
return 0;
}
This is an example of a static method that will take a String that is your resource ID and the type of resource it is so you'd call it..
myText.setText( getResourceId("DESC_WORD_WORD", R.strings.class));
i implemented a search text for my list,
Every list row item includes a text and image that i downloaded from server.
i alsways keeps two list, one is the original List of text and images and other one is the sorted Lists of text and image...
if my list containes only 4 items, so my app works well, however if my list containes 20 items and every image is 1MB and i need to keep two lists listImagesOriginal , image_array inside adapter ( = 40MB) i recieve a OutOfMemory Exception.
how can i reduce the memory consuption that i will not recive a OutOfMemory Exception?
thanks alot
how can i reduce the
private MyCustomAdapter adapter;
private List<String> text = new ArrayList<String>();
private List<Bitmap> listImages;
private List<String> textOriginal = new ArrayList<String>();
private List<Bitmap> listImagesOriginal =new ArrayList<Bitmap>();
adapter =new MyCustomAdapter(text,listImages);
ListView.setAdapter(adapter);
searchText= (EditText) findViewById(R.id.profile_page_search);
searchText.addTextChangedListener(new TextWatcher()
{
public void afterTextChanged(Editable s)
{
// Abstract Method of TextWatcher Interface.
textlength = searchText.getText().length();
adapter.deleteAllForSearch();
for (int i = 0; i < textOriginal.size(); i++)
{
if(textOriginal.get(i).contains(
searchText.getText().toString().trim()))
{
adapter.addObjectForSearch(textOriginal.get(i), listImagesOriginal.get(i));
}
}
}
public void beforeTextChanged(CharSequence s,
int start, int count, int after)
{
// Abstract Method of TextWatcher Interface.
}
public void onTextChanged(CharSequence s,
int start, int before, int count)
{
}
});
class MyCustomAdapter extends BaseAdapter{
public List<String> text_array = new ArrayList<String>();
public List<Bitmap> image_array = new ArrayList<Bitmap>();
public int getCount(){
return text_array.size();
}
MyCustomAdapter(List<String> text, List<Bitmap>)
{
text_array = text;
image_array = image;
}
public long getItemId(int position){
return position;
}
public String getItem(int position){
return null;
}
public View getView(final int position, View convertView, ViewGroup parent) {
//my implementation
}
public void addObjectForSearch(String text, Bitmap bitmap) {
text_array.add(text);
image_array.add(bitmap);
notifyDataSetChanged();
}
40MB is way too much for android. Every phone has a per app memory limit, 40MB is higher than even most high end phones use. You need to implement disk and/or memory caching of your images.
You need to use a cache and/or store them into persistent memory (DB, filesystem, etc), as you don't want all those bitmaps loaded into memory at the same time, especially if you don't see them all at the same time. Many older devices have VM heap sizes around 16-24MB, and you need to be able to stay within a reasonable amount of memory usage.
http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html