RecyclerView + MediaPlayer + Toggle Button + String Uri

放肆的年华 提交于 2019-12-23 10:15:16

问题


Background: I'm working on an Fitness app. Everything is working good till now but problem came when I was working with audio files MediaPlayer in android.

I have checked resources and found ListView but couldn't find anything on RecyclerView + MediaPlayer.

I want to know how to make it work while working with RecyclerView + Toggle Button + String Uri (Offline - Raw folder)

Problem: Right now it is playing the first .mp3 file on every click event (for Eng: R.raw.sample_one_eng is played and for Hindi: R.raw.sample_one_hindi is played). I think it is not taking int position into consideration.

Later, I would like to put it online (may be google cloud) since audio files (.mp3) are making my app quite heavy. Any ideas on that would be appreciated too (fast buffering, etc.).

ListExercises.java

public class ListExercises extends AppCompatActivity {

    List<ExerciseAudio> exerciseList = new ArrayList<>();
    RecyclerView.LayoutManager layoutManager;
    RecyclerView recyclerView;
    RecyclerViewAdapterAud adapter;
    PlayClickHandler clickHandler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.list_exercises);

        initData();

        recyclerView = (RecyclerView) findViewById(R.id.list_ex);
        adapter = new RecyclerViewAdapterAud(exerciseList, getBaseContext());
        layoutManager = new LinearLayoutManager(this);
        recyclerView.setLayoutManager(layoutManager);
        recyclerView.setAdapter(adapter);
    }

    private void initData() {

        exerciseList.add(new ExerciseAudio(R.drawable.sample_one, "Sample Exercise One", "Sans One",
                R.raw.sample_one_eng,
                R.raw.sample_one_hindi));

        exerciseList.add(new ExerciseAudio(R.drawable.sample_two, "Sample Exercise Two", "Sans Two",
                R.raw.sample_two_eng,
                R.raw.sample_two_hindi));

        exerciseList.add(new ExerciseAudio(R.drawable.sample_three, "Sample Exercise Three", "Sans Three",
                R.raw.sample_three_eng,
                R.raw.sample_three_hindi));

        exerciseList.add(new ExerciseAudio(R.drawable.sample_four, "Sample Exercise Four", "Sans Four",
                R.raw.sample_four_eng,
                R.raw.sample_four_hindi));
    }
}

ViewExercise.java

public class ViewExercise extends AppCompatActivity {

    int image_id, eng_aud_url, hindi_aud_url;
    String name, sans_name;

    ArrayList<String> arrayList = new ArrayList<>();

    TextView timer, title, sansName;
    ImageView detail_image;
    ToggleButton tg_btn_speaker_eng, tg_btn_speaker_hindi;

    MediaPlayer mp;

    int position;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.view_exercise);

        Field[] field = R.raw.class.getFields();
        for (int i = 0; i < field.length; i++){
            arrayList.add(field[i].getName());
        }

        title = (TextView) findViewById(R.id.title);
        sansName = (TextView) findViewById(R.id.sans_name);
        detail_image = (ImageView) findViewById(R.id.detail_image);

        tg_btn_speaker_eng = (ToggleButton) findViewById(R.id.tg_btn_speaker_eng);
        tg_btn_speaker_hindi = (ToggleButton) findViewById(R.id.tg_btn_speaker_hindi);

        tg_btn_speaker_eng.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //toggleEnglish(tg_btn_speaker_eng.isChecked());
                int positionEng = 0;
                playSongEng(positionEng);
            }
        });

        tg_btn_speaker_hindi.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //toggleHindi(tg_btn_speaker_hindi.isChecked());
                int positionHindi = 0;
                playSongHindi(positionHindi);
            }
        });

        if (getIntent() != null) {
            image_id = getIntent().getIntExtra("image_id", -1);
            name = getIntent().getStringExtra("name");
            sans_name = getIntent().getStringExtra("sanskrit_name");

            detail_image.setImageResource(image_id);
            title.setText(name);
            sansName.setText(sans_name);
        }
    }


    public void playSongEng(int i) {
        //mp.reset();
        int resId = getResources().getIdentifier(arrayList.get(i), "raw", getPackageName());
        mp = MediaPlayer.create(getApplicationContext(), resId);
        mp.start();
    }

    public void playSongHindi(int i) {
        //mp.reset();
        int resId = getResources().getIdentifier(arrayList.get(i), "raw", getPackageName());
        mp = MediaPlayer.create(getApplicationContext(), resId);
        mp.start();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mp != null)
            mp.release();
    }
}

This is how it looks!

Edit (to avoid confusion): Just noticed, that image name would have been List Exercises instead of View Exercise which corresponds to ListExercises.java

And this one corresponds to ViewExercise.java

Thanks!


回答1:


You can also set the click listener inside onBindViewHolder without using any interface by setting the click listener on parent view of your item_exercise_aud layout like this:

holder.parentView.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Intent intent = new Intent(context, ViewExercise.class);
            intent.putExtra("image_id", exerciseList.get(position).getImage_id());
            intent.putExtra("name", exerciseList.get(position).getName());
            intent.putExtra("sanskrit_name", exerciseList.get(position).getSanskrit_name());
            intent.putExtra("eng_aud_url", exerciseList.get(position).getEng_aud_url());
            intent.putExtra("hindi_aud_url", exerciseList.get(position).getHindi_aud_url());
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            context.startActivity(intent);
        }
    });

Or you can refer here

Edit - To play the audio according to the recyclerview list's position, we have to send the raw file name of that particular exercise to the media player.

Add two new variables to ExerciseAudio model class:

private String file_name_eng;
private String file_name_hindi;

Now modify the initData method as below:

private void initData() {

    exerciseList.add(new ExerciseAudio(R.drawable.sample_one, "Sample Exercise One", "Sans One",
            R.raw.sample_one_eng, R.raw.sample_one_hindi,
            "sample_one_eng", "sample_one_hindi"));

    exerciseList.add(new ExerciseAudio(R.drawable.sample_two, "Sample Exercise Two", "Sans Two",
            R.raw.sample_two_eng, R.raw.sample_two_hindi,
            "sample_two_eng", "sample_one_hindi"));

    exerciseList.add(new ExerciseAudio(R.drawable.sample_three, "Sample Exercise Three", "Sans Three",
            R.raw.sample_three_eng, R.raw.sample_three_hindi,
            "sample_three_eng", "sample_three_hindi"));

    exerciseList.add(new ExerciseAudio(R.drawable.sample_four, "Sample Exercise Four", "Sans Four",
            R.raw.sample_four_eng, R.raw.sample_four_hindi,
            "sample_four_eng", "sample_four_hindi"));
}

Now Modify the ViewExercise intent as:

intent.putExtra("file_name_eng", exerciseList.get(position).getFile_name_eng());
intent.putExtra("file_name_hindi", exerciseList.get(position).getFile_name_hindi());

Get the string extras in ViewExercise activity and send it to the mediaplayer:

file_name_eng = getIntent().getStringExtra("file_name_eng");
file_name_hindi = getIntent().getStringExtra("file_name_hindi");

For english:
    int resId = getResources().getIdentifier(file_name_eng, "raw", getPackageName());

For hindi:
    int resId = getResources().getIdentifier(file_name_hindi, "raw", getPackageName());



回答2:


Try this code .. add below dependency into app level gradle file..

    compile 'com.google.android.exoplayer:exoplayer:r2.3.0'

after that used below code ..

public class VideoPlayerActivity extends AppCompatActivity implements ExoPlayer.EventListener {

private SimpleExoPlayer mSimpleExoPlayer;

private SimpleExoPlayerView mSimpleExoPlayerView;

private Handler mMainHandler;
private AdaptiveTrackSelection.Factory mAdaptiveTrackSelectionFactory;
private TrackSelector mTrackSelector;
private LoadControl mLoadControl;
private DefaultBandwidthMeter mBandwidthMeter;
private DataSource.Factory mDataSourceFactory;
private SimpleCache mSimpleCache;
private DataSource.Factory mFactory;
private MediaSource mVideoSource;
private LoopingMediaSource mLoopingMediaSource;
private ProgressBar mProgressBar;
private String videoUrl="http://sample.vodobox.net/skate_phantom_flex_4k/skate_phantom_flex_4k.m3u8";

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_video);
    mSimpleExoPlayerView = (SimpleExoPlayerView) findViewById(R.id.videoPlayer);
    mProgressBar = (ProgressBar) findViewById(R.id.amPrgbrLoading);
}

/**
 * this method play audio and video with hls streaming.
 */
private void playMedia() {
    mMainHandler = new Handler();
    mBandwidthMeter = new DefaultBandwidthMeter();
    mAdaptiveTrackSelectionFactory = new AdaptiveTrackSelection.Factory(mBandwidthMeter);
    mTrackSelector = new DefaultTrackSelector(mAdaptiveTrackSelectionFactory);

    mLoadControl = new DefaultLoadControl();
    mSimpleExoPlayer = ExoPlayerFactory.newSimpleInstance(this, mTrackSelector, mLoadControl);

    mSimpleExoPlayerView.setPlayer(mSimpleExoPlayer);
    mSimpleExoPlayerView.setControllerVisibilityListener(new PlaybackControlView.VisibilityListener() {
        @Override
        public void onVisibilityChange(int visibility) {
            mSimpleExoPlayerView.showController();
        }
    });
    mDataSourceFactory = new DefaultDataSourceFactory(this,Util.getUserAgent(this, "com.exoplayerdemo"), mBandwidthMeter);
    mSimpleCache = new SimpleCache(this.getCacheDir(), new LeastRecentlyUsedCacheEvictor(1024 * 1024 * 10));
    mFactory = new CacheDataSourceFactory(mSimpleCache, mDataSourceFactory,0);
    mVideoSource = new HlsMediaSource(Uri.parse(videoUrl),
            mFactory, mMainHandler, new AdaptiveMediaSourceEventListener() {
        @Override
        public void onLoadStarted(DataSpec dataSpec, int dataType, int trackType, Format trackFormat, int trackSelectionReason, Object trackSelectionData, long mediaStartTimeMs, long mediaEndTimeMs, long elapsedRealtimeMs) {
            //Toast.makeText(VideoPlayerActivity.this, "Load Started", Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onLoadCompleted(DataSpec dataSpec, int dataType, int trackType, Format trackFormat, int trackSelectionReason, Object trackSelectionData, long mediaStartTimeMs, long mediaEndTimeMs, long elapsedRealtimeMs, long loadDurationMs, long bytesLoaded) {
            //Toast.makeText(VideoPlayerActivity.this, "Load Completed", Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onLoadCanceled(DataSpec dataSpec, int dataType, int trackType, Format trackFormat, int trackSelectionReason, Object trackSelectionData, long mediaStartTimeMs, long mediaEndTimeMs, long elapsedRealtimeMs, long loadDurationMs, long bytesLoaded) {

        }

        @Override
        public void onLoadError(DataSpec dataSpec, int dataType, int trackType, Format trackFormat, int trackSelectionReason, Object trackSelectionData, long mediaStartTimeMs, long mediaEndTimeMs, long elapsedRealtimeMs, long loadDurationMs, long bytesLoaded, IOException error, boolean wasCanceled) {
            videoUrl=""; // define second url
        }

        @Override
        public void onUpstreamDiscarded(int trackType, long mediaStartTimeMs, long mediaEndTimeMs) {
           // Toast.makeText(VideoPlayerActivity.this, "Up stream", Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onDownstreamFormatChanged(int trackType, Format trackFormat, int trackSelectionReason, Object trackSelectionData, long mediaTimeMs) {
           // Toast.makeText(VideoPlayerActivity.this, "Down Stream", Toast.LENGTH_SHORT).show();
        }
    });

    mLoopingMediaSource = new LoopingMediaSource(mVideoSource);
    mSimpleExoPlayer.prepare(mLoopingMediaSource);
    mSimpleExoPlayer.setPlayWhenReady(true);
    mSimpleExoPlayer.addListener(new ExoPlayer.EventListener() {
        @Override
        public void onTimelineChanged(Timeline timeline, Object manifest) {

        }

        @Override
        public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {

        }

        @Override
        public void onLoadingChanged(boolean isLoading) {

        }

        @Override
        public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {

            if (playbackState == ExoPlayer.STATE_BUFFERING) {
                mProgressBar.setVisibility(View.VISIBLE);
            } else {
                mProgressBar.setVisibility(View.GONE);
            }

        }

        @Override
        public void onPlayerError(ExoPlaybackException error) {

        }

        @Override
        public void onPositionDiscontinuity() {

        }
    });



}

@Override
protected void onResume() {
    super.onResume();
    playMedia();
}

@Override
protected void onStop() {
    super.onStop();
    stopMedia();
}

@Override
protected void onPause() {
    super.onPause();
    stopMedia();
}

private void stopMedia() {
    mSimpleExoPlayer.stop();
    mSimpleExoPlayer.release();
}



@Override
public void onTimelineChanged(Timeline timeline, Object manifest) {

}

@Override
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {

}

@Override
public void onLoadingChanged(boolean isLoading) {

}

@Override
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {

}

@Override
public void onPlayerError(ExoPlaybackException error) {

}

@Override
public void onPositionDiscontinuity() {

}

}

in this code used for video you can pass yor audio file path..

xml code..

        <com.google.android.exoplayer2.ui.SimpleExoPlayerView
        android:id="@+id/videoPlayer"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
         />



回答3:


You should design the ViewHolder of the RecyclerView in such a way that it should hold all the data required for the recycler view to work. For e.g

ExcerciseViewHolder extends ViewHolder {
 public int uriEnglish;  //To be replaced by URL/URI later on
 public int uriHindi; //To be replaced by URL/URI later on
 public View englishButton;
 public View hindiButton;
}

Your activity should implement an interface in the form of

public interface PlayClickHandler {
public playInstruction(int uriID);
}

Your adapter should be something like this:

public class ExerciseAdapter extends RecyclerView.Adapter<ExerciseViewHolder> {
    PlayClickHandler clickHandler;
    public ExerciseAdapter(PlayClickHandler clickHandler){
        this.clickHandler = clickHandler;
    }
    ...
    public onBindViewHolder(ExerciseViewHolder holder, int position) {
        final ExcerciseViewHolder finalHolder = holder;
        holder.hindiButton.setOnClickListener(new View.OnClickListener() {
           @Override
           public void onClick(View v) {
           clickHandler.playInstruction(finalHolder.uriHindi);
           }
        })
    }
}

Your Activity should be implementing this interface and provide the functionality for playback using a single instance of MediaPlayer.

Following the idea in the above code, I believe you should be able to achieve what you want. Please feel free to ask if you are not clear on any point.



来源:https://stackoverflow.com/questions/51111733/recyclerview-mediaplayer-toggle-button-string-uri

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!