I have a list view of songs with play and pause button in every row.
Obviously, I can't have two pause Icon(two playing song)at the same time in my list view So I need to first reset all of them to play Icon then set the selected view to pause Icon.
How can I achieve this?
This is what I have done so far:
In the model class ( Product ):
public boolean paused = true;
private int PlayPauseId;
public int getPlayPauseId(){
return PlayPauseId;
}
public void setPlayPauseId(int playPauseId) {
PlayPauseId = playPauseId;
}
in Adapter:
public interface PlayPauseClick {
void playPauseOnClick(int position);
}
private PlayPauseClick callback;
public void setPlayPauseClickListener(PlayPauseClick listener) {
this.callback = listener;
}
.
.
.
holder.playPauseHive.setImageResource(product.getPlayPauseId());
holder.playPauseHive.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (callback != null) {
callback.playPauseOnClick(position);
}
}
});
my Callback inside my Activity:
#Override
public void playPauseOnClick(int position) {
final Product product = movieList.get(position);
if (product.paused) {
product.setPlayPauseId(R.drawable.ic_pause);
product.paused=false;
}else {
product.setPlayPauseId(R.drawable.ic_play);
product.paused = true;
}
this.adapter.notifyDataSetChanged();
}
You have to check condition in your getView() method of adapter like this:
if (product.paused) {
holder.playPauseHive.setImageResource(R.drawable.ic_play);
}else {
holder.playPauseHive.setImageResource(R.drawable.ic_pause);
}
What i created while making CHROMELESS youtube player view is here. It is loading and playing perfectly but the buffering circle plays continuously even when video gets completed. Can anyone help me to remove buffering progress when it is not required and make a perfect youtube player?
MainActivity.java
package com.rocky.youtubedemo;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.widget.Toast;
import com.google.android.youtube.player.YouTubeBaseActivity;
import com.google.android.youtube.player.YouTubeInitializationResult;
import com.google.android.youtube.player.YouTubePlayer;
import com.google.android.youtube.player.YouTubePlayerView;
public class MainActivity extends YouTubeBaseActivity implements YouTubePlayer.OnInitializedListener {
private static final int RECOVERY_REQUEST = 1;
private static String YOUTUBE_API_KEY = "";
private YouTubePlayerView youTubeView;
private Context context;
private MyPlayerStateChangeListener playerStateChangeListener;
private MyPlaybackEventListener playbackEventListener;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
context = this;
playerStateChangeListener = new MyPlayerStateChangeListener();
playbackEventListener = new MyPlaybackEventListener();
YOUTUBE_API_KEY = "PLACE_YOUR_API_KEY_HERE";
youTubeView = (YouTubePlayerView) findViewById(R.id.youtube_view);
youTubeView.initialize(YOUTUBE_API_KEY, this);
}
#Override
public void onInitializationSuccess(YouTubePlayer.Provider provider, final YouTubePlayer player, boolean wasRestored) {
player.setPlayerStateChangeListener(playerStateChangeListener);
player.setPlaybackEventListener(playbackEventListener);
if (!wasRestored) {
player.loadVideo("fhWaJi1Hsfo"); // Plays https://www.youtube.com/watch?v=fhWaJi1Hsfo
player.setPlayerStyle(YouTubePlayer.PlayerStyle.CHROMELESS);
}
}
#Override
public void onInitializationFailure(YouTubePlayer.Provider provider, YouTubeInitializationResult errorReason) {
if (errorReason.isUserRecoverableError()) {
Toast.makeText(context, errorReason.toString(), Toast.LENGTH_LONG).show();
// errorReason.getErrorDialog(this, RECOVERY_REQUEST).show();
} else {
String error = errorReason.toString();
Toast.makeText(context, error, Toast.LENGTH_LONG).show();
}
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == RECOVERY_REQUEST) {
// Retry initialization if user performed a recovery action
getYouTubePlayerProvider().initialize(YOUTUBE_API_KEY, this);
}
}
private void showMessage(String message) {
Toast.makeText(this, message, Toast.LENGTH_LONG).show();
}
protected YouTubePlayer.Provider getYouTubePlayerProvider() {
return youTubeView;
}
private final class MyPlaybackEventListener implements YouTubePlayer.PlaybackEventListener {
#Override
public void onPlaying() {
// Called when playback starts, either due to user action or call to play().
showMessage("Playing");
}
#Override
public void onPaused() {
// Called when playback is paused, either due to user action or call to pause().
showMessage("Paused");
}
#Override
public void onStopped() {
// Called when playback stops for a reason other than being paused.
showMessage("Stopped");
}
#Override
public void onBuffering(boolean b) {
showMessage("buffer");
}
#Override
public void onSeekTo(int i) {
// Called when a jump in playback position occurs, either
// due to user scrubbing or call to seekRelativeMillis() or seekToMillis()
}
}
private final class MyPlayerStateChangeListener implements YouTubePlayer.PlayerStateChangeListener {
#Override
public void onLoading() {
showMessage("loading");
}
#Override
public void onLoaded(String s) {
showMessage("loaded");
}
#Override
public void onAdStarted() {
// Called when playback of an advertisement starts.
}
#Override
public void onVideoStarted() {
// Called when playback of the video starts.
showMessage("started");
}
#Override
public void onVideoEnded() {
// Called when the video reaches its end.
}
#Override
public void onError(YouTubePlayer.ErrorReason errorReason) {
// Called when an error occurs.
}
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.google.android.youtube.player.YouTubePlayerView
android:id="#+id/youtube_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</RelativeLayout>
In your onInitializationSuccess(), set a PlaybackEventListener on the player. Override the onBuffering() and do something like this:
ViewGroup ytView = youTubeView; // if you are using YouTubePlayerView
ViewGroup ytView = (ViewGroup)ytPlayerFragment.getView(); // if you are using YouTubePlayerFragment
ProgressBar progressBar;
try {
// As of 2016-02-16, the ProgressBar is at position 0 -> 3 -> 2 in the view tree of the Youtube Player Fragment/View
ViewGroup child1 = (ViewGroup)ytView.getChildAt(0);
ViewGroup child2 = (ViewGroup)child1.getChildAt(3);
progressBar = (ProgressBar)child2.getChildAt(2);
} catch (Throwable t) {
// As its position may change, we fallback to looking for it
progressBar = findProgressBar(ytView);
// I recommend reporting this problem so that you can update the code in the try branch: direct access is more efficient than searching for it
}
int visibility = isBuffering ? View.VISIBLE : View.INVISIBLE;
if (progressBar != null) {
progressBar.setVisibility(visibility);
// Note that you could store the ProgressBar instance somewhere from here, and use that later instead of accessing it again.
}
Make method like this one:
private ProgressBar findProgressBar(View view) {
if (view instanceof ProgressBar) {
return (ProgressBar)view;
} else if (view instanceof ViewGroup) {
ViewGroup viewGroup = (ViewGroup)view;
for (int i = 0; i < viewGroup.getChildCount(); i++) {
ProgressBar res = findProgressBar(viewGroup.getChildAt(i));
if (res != null) return res;
}
}
return null
}
By this way you can enable progress when it is buffering and disable it when it is not.
Help please find memory leaks in my app.
LeakCanary:
-static android.app.ActivityThread.sCurrentActivityThread
-references android.utill.ArrayMap.mArray
-reference array java.lang.Object[].1
-references android.app.ActivityThread$ActivityClientRecord.activity
-references MainActivity.tf (tf - my fragment)
-leaks TheaterFragment instance
Example code fragment:
public class TheaterFragment extends Fragment {
Typeface face;
private View view;
private RelativeLayout rl;
private LinearLayout linearLayout;
private int wordLheigh, screenWidht, wordLength, margin, btnHeight;
private String word;
private float scale;
private Animation anim;
private int tangelSound, whooshSound;
private SoundPool mSoundPool2;
private int soundMusic;
private boolean startAnim = false;
private LayoutAnimationController controller;
#Override
public void onStart(){
super.onStart();
if (!word.equals("suoh") && startAnim) {
anim = AnimationUtils.loadAnimation(getActivity(), R.anim.theater);
anim.setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
mSoundPool2.play(whooshSound, soundMusic, soundMusic, 0, 0, 1);
rl.setClickable(false);
}
#Override
public void onAnimationRepeat(Animation animation) {
}
#Override
public void onAnimationEnd(Animation animation) {
// linearLayout.startLayoutAnimation();
startAnim = false;
Animation anim1 = AnimationUtils.loadAnimation(getActivity(), R.anim.blueword);
anim1.setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
mSoundPool2.play(tangelSound, soundMusic, soundMusic, 0, wordLength, 0.5f);
}
#Override
public void onAnimationRepeat(Animation animation) {
}
#Override
public void onAnimationEnd(Animation animation) {
// rl.setClickable(true);
}
});
controller = new LayoutAnimationController(anim1, 0.5f);
linearLayout.setLayoutAnimation(controller);
linearLayout.setLayoutAnimationListener(new Animation.AnimationListener() {
public void onAnimationEnd(Animation _animation) {
rl.setClickable(true);
}
public void onAnimationRepeat(Animation _animation) {
}
public void onAnimationStart(Animation _animation) {
}
});
linearLayout.setVisibility(View.VISIBLE);
linearLayout.startLayoutAnimation();
}
});
rl.setVisibility(View.VISIBLE);
rl.startAnimation(anim);
}
}
public void falseClickable(){
rl.setClickable(false);
rl.setEnabled(false);
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
face = Typefaces.get(getContext(), getContext().getText(R.string.font_droidsans).toString());
if (Build.VERSION.SDK_INT >= 21)
{
AudioAttributes attributes = new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_GAME)
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.build();
mSoundPool2 = new SoundPool.Builder()
.setAudioAttributes(attributes)
.build();
} else {
mSoundPool2 = new SoundPool(5, AudioManager.STREAM_MUSIC, 0);
}
tangelSound = mSoundPool2.load(getActivity(), R.raw.tangel, 1);
whooshSound = mSoundPool2.load(getActivity(), R.raw.whoosh3, 1);
scale = getArguments().getInt("scale");
wordLheigh = getArguments().getInt("wordLheigh");
screenWidht = getArguments().getInt("screenWidht");
word = getArguments().getString("word");
wordLength = word.length();
soundMusic = getArguments().getInt("soundMusic");
startAnim = getArguments().getBoolean("startAnim");
//Log.e("TylyTaay", Integer.toString(FINAL));
margin = (int) (getResources().getDimension(R.dimen.word_btn_margin)+0.5f);
btnHeight = (int) (screenWidht / 10 - 4 * margin +0.5f);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
view = inflater.inflate(R.layout.fragment_theater, container, false);
rl = (RelativeLayout) view.findViewById(R.id.theaterLayout);
TextView tv1 = (TextView) view.findViewById(R.id.textView1);
TextView tv2 = (TextView) view.findViewById(R.id.textView1);
tv1.setTypeface(face);
tv2.setTypeface(face);
if (!word.equals("suoh")){
linearLayout = (LinearLayout) view.findViewById(R.id.clonWordLayout);
RelativeLayout.LayoutParams lparams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, wordLheigh);
lparams.setMargins(0, screenWidht, 0, 0);
linearLayout.setLayoutParams(lparams);
setClon();
} else {
rl.setVisibility(View.VISIBLE);
}
return view;
}
#Override
public void onDestroy() {
super.onDestroy();
linearLayout.removeAllViews();
linearLayout = null;
RefWatcher refWatcher = MyApp.getRefWatcher(getActivity());
refWatcher.watch(this);
}
private void setClon()
{
Button btn;
for(int i = 0; i < wordLength; i++)
{
btn = new Button(getActivity());
char[] ch = {word.charAt(i)};
String st = new String(ch);
btn.setText(st);
btn.setGravity(Gravity.CENTER);
btn.setPadding(0, 0, 0, 0);
btn.setTextSize(TypedValue.COMPLEX_UNIT_PX, getResources().getDimension(R.dimen.word_btn_text_size));
btn.setBackgroundResource(R.drawable.btn_word_win);
btn.setTextColor(getResources().getColorStateList(R.color.dark_gray));
btn.setClickable(false);
btn.setTypeface(face);
LinearLayout.LayoutParams lparams = new LinearLayout.LayoutParams(btnHeight, btnHeight);
lparams.setMargins(margin, 0,margin, 0);
linearLayout.addView(btn, lparams); //Добавляем в LinearLayout
}
btn = null;
}
public void playSound(int nameSound ) {
if (soundMusic == 1)
mSoundPool2.play(nameSound,1,1,0,0,1);}
The sample code in MainActivity, which call the fragment:
private void rightAnswer()
{
if (imgZoom != null) {
imgZoomBack(wordL);
}
wordL.blockWord(false);
buttonL.blockBtn(false);
gridView.setOnItemClickListener(null);
//tf = new TheaterFragment(scale, wordLheigh, screenWidht, word, bar.tvDiamond, bar.tvLevel, bar.imageView, wordId);
if (tf == null) {
tf = new TheaterFragment();
}
if (args == null) {
args = new Bundle();
}
args.putFloat("scale", scale);
args.putInt("wordLheigh", wordLheight);
args.putInt("screenWidht", screenWidht);
args.putInt("soundMusic", soundOnOff);
args.putBoolean("startAnim", true);
args.putString("word", word);
//Log.e("TylyTaay", Integer.toString(wordId));
args.putInt("wordId", wordId);
tf.setArguments(args);
args = null;
getSupportFragmentManager()
.beginTransaction()
.add(R.id.frameLayout, tf)
.commit();
YoYo.with(Techniques.SlideOutDown)
.playOn(bar.tgDiamond);
YoYo.with(Techniques.SlideOutDown)
.playOn(bar.tgVolume);
YoYo.with(Techniques.SlideOutDown)
.playOn(bar.tvDiamond);
YoYo.with(Techniques.SlideOutDown)
.playOn(bar.tvLevel);
bar.imageView.setVisibility(View.VISIBLE);
YoYo.with(Techniques.SlideInDown)
.playOn(bar.imageView);}
I'm late for the response, this might be helpful for others.
As mentioned by #tsiro LeakCanary has its own bugs, you can go here for more details.
For your code,
getSupportFragmentManager()
.beginTransaction()
.add(R.id.frameLayout, tf)
.commit();
Here you are adding the fragment in FragmentManager but you are not removing it when the activity goes to background or when the fragment is changed/reinitialised.
Try removing the fragment from the fragment manager, if you are using .add().
You can use .replace() which calls .remove() and .add() implicitly.
I hope it helps.
i have just noticed that i take the same output in my application.Even though i double checked my app for possible memory leaks and after i resolve the actual point in my code where memory leak occured, i am still getting this message...you do not have to worry about it, probably it is a bug created by LeakCanary library...just ignore it as i did..
I have searched but could not find answer of my question.
This is what I have:
private class BoxView extends View {
private String caption;
private OnClickListener bvClickListener = null
public BoxView(Context context) {
super(context);
this.bvClickListener = new this.OnClickListener(){
public void onClick (View v){
/*v.setCaption("X"); view don't have this method */
}}
}
public void setCaption(String s){
this.caption=s;
invalidate();
}
}
This is what I want to have:
private class BoxView extends View {
private String caption;
private OnClickListener bvClickListener = null
public BoxView(Context context) {
super(context);
this.bvClickListener = new this.OnClickListener(){
public void onClick (BoxView bv){
bv.setCaption("X");
}}
}
public void setCaption(String s){
this.caption=s;
invalidate();
}
}
I may need custom methods for my custom views. And I want to be able to pass my custom view instead of view version of it when onclick is triggered so I can access to it directly.
Updated
And I want to have access to real object not a converted one. So I want to avoid this:
public void onClick (View v){
((BoxView)v).setCaption("X");
}
Call setCaption method as in onClick :
public void onClick (View v){
((BoxView)v).setCaption("X");
}
Try this
class Main extents Activity
{
BoxView boxView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// if view is used using layout then
boxView = (BoxView)findViewByID(id);
//else if directly used
boxView = new BoxView(this);
box.setOnClickListener(new onClickListener()
{
#Override
public void onClick(View view) {
boxView.setCaption("X");
boxView.invalidate();
}
});
}
}
Now that I can animate my FloatingActionButton, I have another issue.
When I click on the FAB, the rotation of the FAB starts and keeps going until an AsyncTask finishes its job.
Here are the parts of the code related:
#OnClick(R.id.floating_action_button) //Butterknife feature
public void refreshAccountInfo(){
APIHandler api = new APIHandler(databaseHelper, c, getActivity());
AccountSync accountSync = new AccountSync(api);
accountSync.execute();
}
public class AccountSync extends AsyncTask<Void,Void,Account> {
APIHandler apiHandler;
ObjectAnimator animation;
public AccountSync(APIHandler api){
apiHandler = api;
}
#Override
protected void onPreExecute(){
FloatingActionButton button = ButterKnife.findById(getActivity(), R.id.floating_action_button);
PropertyValuesHolder pvhR = PropertyValuesHolder.ofFloat(View.ROTATION, -360);
animation = ObjectAnimator.ofPropertyValuesHolder(button, pvhR);
animation.setRepeatCount(Animation.INFINITE);
animation.setDuration(1500);
animation.start();
}
#Override
protected Account doInBackground(Void... params) {
Account a;
try {
a = apiHandler.getAccountInfo();
return a;
} catch (final ResponseException e) {
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
Toast.makeText(apiHandler.getContext(), e.getMessage(), Toast.LENGTH_LONG).show();
}
});
return null;
}
}
#Override
protected void onPostExecute(Account result) {
animation.setRepeatCount(0);
if(result!=null){
a = result;
showInfo();
Toast.makeText(getActivity(),"Account synchronization done",Toast.LENGTH_SHORT).show();
}
}
}
My problem is, when I trigger the button the first time, the animation plays fine, but when I try to click more times, the AsyncTask will do its job, but the rotation animation will never play until I reload the fragment completely.
How can I fix that ?
You can for example end the animation in onPostExecute like this :
#Override
protected void onPostExecute(Account result) {
if(animation.isRunning())
animation.end();
...
}
}
That should be more efficient than setting repeatCount to 0.