Belajar Aplikasi Android Studi Kasus Proyek Akademi : Menghubungkan Activity & Fragment
Codelab Menghubungkan Activity dan Fragment
Pada codelab ini Anda akan menghubungkan tiap asset-asset dan layout dengan fragment dan Activity. Mari kita mulai dengan mengubah kode yang ada di dalam package ui:
- Buatlah sebuah kelas AcademyAdapter untuk menampilkan item untuk RecyclerView di package academy.
Tambahkanlah kode berikut untuk setCourses, membuat kelas ViewHolder, mem-binding ViewHolder dan mengirim data ke DetailActivity.
Pada kode di atas akan terjadi error pada bagian EXTRA_COURSE, klik ALT + Enter kemudian pilih:Kotlin class AcademyAdapter : RecyclerView.Adapter<AcademyAdapter.CourseViewHolder>() {
private var listCourses = ArrayList<CourseEntity>()
fun setCourses(courses: List<CourseEntity>?) {
if (courses == null) return
listCourses.clear()
listCourses.addAll(courses)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CourseViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.items_academy, parent, false)
return CourseViewHolder(view)
}
override fun onBindViewHolder(holder: CourseViewHolder, position: Int) {
val course = listCourses[position]
holder.bind(course)
}
override fun getItemCount(): Int = listCourses.size
class CourseViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bind(course: CourseEntity) {
with(itemView) {
tv_item_title.text = course.title
tv_item_description.text = course.description
tv_item_date.text = resources.getString(R.string.deadline_date, course.deadline)
setOnClickListener {
val intent = Intent(context, DetailCourseActivity::class.java).apply {
putExtra(DetailCourseActivity.EXTRA_COURSE, course.courseId)
}
context.startActivity(intent)
}
Glide.with(context)
.load(course.imagePath)
.apply(RequestOptions.placeholderOf(R.drawable.ic_loading)
.error(R.drawable.ic_error))
.into(img_poster)
}
}
}
}Java public class AcademyAdapter extends RecyclerView.Adapter<AcademyAdapter.CourseViewHolder> {
private List<CourseEntity> listCourses = new ArrayList<>();
void setCourses(List<CourseEntity> listCourses) {
if (listCourses == null) return;
listCourses.clear();
listCourses.addAll(listCourses);
}
@NonNull
@Override
public CourseViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.items_academy, parent, false);
return new CourseViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull final CourseViewHolder holder, int position) {
CourseEntity course = listCourses.get(position);
holder.bind(course);
}
@Override
public int getItemCount() {
return listCourses.size();
}
class CourseViewHolder extends RecyclerView.ViewHolder {
final TextView tvTitle;
final TextView tvDescription;
final TextView tvDate;
final ImageView imgPoster;
CourseViewHolder(View itemView) {
super(itemView);
tvTitle = itemView.findViewById(R.id.tv_item_title);
imgPoster = itemView.findViewById(R.id.img_poster);
tvDescription = itemView.findViewById(R.id.tv_item_description);
tvDate = itemView.findViewById(R.id.tv_item_date);
}
void bind(CourseEntity course) {
tvTitle.setText(course.getTitle());
tvDescription.setText(course.getDescription());
tvDate.setText(itemView.getResources().getString(R.string.deadline_date, course.getDeadline()));
itemView.setOnClickListener(v -> {
Intent intent = new Intent(itemView.getContext(), DetailCourseActivity.class);
intent.putExtra(DetailCourseActivity.EXTRA_COURSE, course.getCourseId());
itemView.getContext().startActivity(intent);
});
Glide.with(itemView.getContext())
.load(course.getImagePath())
.apply(RequestOptions.placeholderOf(R.drawable.ic_loading).error(R.drawable.ic_error))
.into(imgPoster);
}
}
}
Setelah itu masukkan “extra_course” sebagai value-nya.Kotlin Java Kotlin companion object {
const val EXTRA_COURSE = "extra_course"
}Java public static final String EXTRA_COURSE = "extra_course";
- Setelah Anda membuat kelas Adapter, tambahkan kode berikut di dalam AcademyFragment untuk menghubungkan fragment dengan RecyclerView.
Dengan begitu, kelas AcademyFragment sudah menampilkan data dari kelas DataDummy.Kotlin class AcademyFragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? =
inflater.inflate(R.layout.fragment_academy, container, false)
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
if (activity != null) {
val courses = DataDummy.generateDummyCourses()
val academyAdapter = AcademyAdapter()
academyAdapter.setCourses(courses)
with(rv_academy) {
layoutManager = LinearLayoutManager(context)
setHasFixedSize(true)
adapter = academyAdapter
}
}
}
}Java public class AcademyFragment extends Fragment {
private RecyclerView rvCourse;
private ProgressBar progressBar;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_academy, container, false);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
rvCourse = view.findViewById(R.id.rv_academy);
progressBar = view.findViewById(R.id.progress_bar);
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (getActivity() != null) {
List<CourseEntity> courses = DataDummy.generateDummyCourses();
AcademyAdapter academyAdapter = new AcademyAdapter();
academyAdapter.setCourses(courses);
rvCourse.setLayoutManager(new LinearLayoutManager(getContext()));
rvCourse.setHasFixedSize(true);
rvCourse.setAdapter(academyAdapter);
}
}
} - Selanjutnya buatlah kembali kelas baru dan beri nama BookmarkAdapter di package bookmark.
Tambahkanlah kode berikut untuk setCourses, membuat kelas ViewHolder, mem-binding ViewHolder dan mengirim data ke DetailActivity.
Pada kode di atas akan terjadi eror pada BookmarkFragmentCallback. Klik ALT + Enter kemudian pilih Create interface BookmarkFragmentCallback’.Kotlin class BookmarkAdapter(private val callback: BookmarkFragmentCallback) : RecyclerView.Adapter<BookmarkAdapter.CourseViewHolder>() {
private val listCourses = ArrayList<CourseEntity>()
fun setCourses(courses: List<CourseEntity>?) {
if (courses == null) return
listCourses.clear()
listCourses.addAll(courses)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CourseViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.items_bookmark, parent, false)
return CourseViewHolder(view)
}
override fun onBindViewHolder(holder: CourseViewHolder, position: Int) {
val course = listCourses[position]
holder.bind(course)
}
override fun getItemCount(): Int = listCourses.size
inner class CourseViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bind(course: CourseEntity){
with(itemView){
tv_item_title.text = course.title
tv_item_description.text = course.description
tv_item_date.text = resources.getString(R.string.deadline_date, course.deadline)
setOnClickListener {
val intent = Intent(context, DetailCourseActivity::class.java).apply {
putExtra(DetailCourseActivity.EXTRA_COURSE, course.courseId)
}
context.startActivity(intent)
}
img_share.setOnClickListener { callback.onShareClick(course) }
Glide.with(context)
.load(course.imagePath)
.apply(RequestOptions.placeholderOf(R.drawable.ic_loading)
.error(R.drawable.ic_error))
.into(img_poster)
}
}
}
}Java public class BookmarkAdapter extends RecyclerView.Adapter<BookmarkAdapter.CourseViewHolder> {
private final BookmarkFragmentCallback callback;
private ArrayList<CourseEntity> listCourses = new ArrayList<>();
BookmarkAdapter(BookmarkFragmentCallback callback) {
this.callback = callback;
}
void setCourses(List<CourseEntity> courses) {
if (courses == null) return;
listCourses.clear();
listCourses.addAll(courses);
}
@NonNull
@Override
public CourseViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.items_bookmark, parent, false);
return new CourseViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull final CourseViewHolder holder, int position) {
CourseEntity course = listCourses.get(position);
holder.bind(course);
}
@Override
public int getItemCount() {
return listCourses.size();
}
class CourseViewHolder extends RecyclerView.ViewHolder {
final TextView tvTitle;
final TextView tvDescription;
final TextView tvDate;
final ImageButton imgShare;
final ImageView imgPoster;
CourseViewHolder(View itemView) {
super(itemView);
tvTitle = itemView.findViewById(R.id.tv_item_title);
tvDescription = itemView.findViewById(R.id.tv_item_description);
tvDate = itemView.findViewById(R.id.tv_item_date);
imgShare = itemView.findViewById(R.id.img_share);
imgPoster = itemView.findViewById(R.id.img_poster);
}
void bind(CourseEntity course) {
tvTitle.setText(course.getTitle());
tvDescription.setText(course.getDescription());
tvDate.setText(itemView.getResources().getString(R.string.deadline_date, course.getDeadline()));
itemView.setOnClickListener(v -> {
Intent intent = new Intent(itemView.getContext(), DetailCourseActivity.class);
intent.putExtra(DetailCourseActivity.EXTRA_COURSE, course.getCourseId());
itemView.getContext().startActivity(intent);
});
imgShare.setOnClickListener(v -> callback.onShareClick(course));
Glide.with(itemView.getContext())
.load(course.getImagePath())
.apply(RequestOptions.placeholderOf(R.drawable.ic_loading).error(R.drawable.ic_error))
.into(imgPoster);
}
}
}
Klik OK, maka secara otomatis akan ada kelas baru di dalam package bookmark.Kotlin Java
Setelah itu tambahkan kode berikut di kelas BookmarkFragmentCallback:Kotlin interface BookmarkFragmentCallback {
fun onShareClick(course: CourseEntity)
}Java interface BookmarkFragmentCallback {
void onShareClick(CourseEntity course);
} - Setelah Anda membuat kelas Adapter, tambahkan kode berikut di dalam BookmarkFragment untuk menghubungkan Fragment dengan RecyclerView.
Dengan begitu, kelas BookmarkFragment sudah bisa menampilkan data dari kelas DataDummy.Kotlin class BookmarkFragment : Fragment(), BookmarkFragmentCallback {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_bookmark, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
if (activity != null) {
val courses = DataDummy.generateDummyCourses()
val adapter = BookmarkAdapter(this)
adapter.setCourses(courses)
with(rv_bookmark) {
layoutManager = LinearLayoutManager(context)
setHasFixedSize(true)
this.adapter = adapter
}
}
}
override fun onShareClick(course: CourseEntity) {
if (activity != null) {
val mimeType = "text/plain"
ShareCompat.IntentBuilder.from(activity).apply {
setType(mimeType)
setChooserTitle("Bagikan aplikasi ini sekarang.")
setText(resources.getString(R.string.share_text, course.title))
startChooser()
}
}
}
}Java public class BookmarkFragment extends Fragment implements BookmarkFragmentCallback {
private RecyclerView rvBookmark;
private ProgressBar progressBar;
public BookmarkFragment() {
// Required empty public constructor
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_bookmark, container, false);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
rvBookmark = view.findViewById(R.id.rv_bookmark);
progressBar = view.findViewById(R.id.progress_bar);
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (getActivity() != null) {
List<CourseEntity> courses = DataDummy.generateDummyCourses();
BookmarkAdapter adapter = new BookmarkAdapter(this);
adapter.setCourses(courses);
rvBookmark.setLayoutManager(new LinearLayoutManager(getContext()));
rvBookmark.setHasFixedSize(true);
rvBookmark.setAdapter(adapter);
}
}
@Override
public void onShareClick(CourseEntity course) {
if (getActivity() != null) {
String mimeType = "text/plain";
ShareCompat.IntentBuilder
.from(getActivity())
.setType(mimeType)
.setChooserTitle("Bagikan aplikasi ini sekarang.")
.setText(getResources().getString(R.string.share_text, course.getTitle()))
.startChooser();
}
}
} - Selanjutnya, buatlah kembali kelas baru dan beri nama DetailCourseAdapter di package detail.
Tambahkanlah kode berikut untuk setModules, membuat kelas ViewHolder, mem-binding ViewHolder dan mengirim data ke CourseReaderActivity.Kotlin class DetailCourseAdapter : RecyclerView.Adapter<DetailCourseAdapter.ModuleViewHolder>() {
private val listModules = ArrayList<ModuleEntity>()
fun setModules(modules: List<ModuleEntity>?) {
if (modules == null) return
listModules.clear()
listModules.addAll(modules)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ModuleViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.items_module_list, parent, false)
return ModuleViewHolder(view)
}
override fun onBindViewHolder(viewHolder: ModuleViewHolder, position: Int) {
val module = listModules[position]
viewHolder.bind(module)
}
override fun getItemCount(): Int = listModules.size
inner class ModuleViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bind(module: ModuleEntity) {
with(itemView) {
text_module_title.text = module.title
}
}
}
}Java public class DetailCourseAdapter extends RecyclerView.Adapter<DetailCourseAdapter.ModuleViewHolder> {
private List<ModuleEntity> listModules = new ArrayList<>();
void setModules(List<ModuleEntity> modules) {
if (modules == null) return;
listModules.clear();
listModules.addAll(modules);
}
@NonNull
@Override
public ModuleViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.items_module_list, parent, false);
return new ModuleViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull ModuleViewHolder viewHolder, int position) {
ModuleEntity module = listModules.get(position);
viewHolder.bind(module);
}
@Override
public int getItemCount() {
return listModules.size();
}
class ModuleViewHolder extends RecyclerView.ViewHolder {
final TextView textTitle;
ModuleViewHolder(View itemView) {
super(itemView);
textTitle = itemView.findViewById(R.id.text_module_title);
}
void bind(ModuleEntity module) {
textTitle.setText(module.getTitle());
}
}
} - Setelah Anda membuat kelas Adapter, tambahkan kode berikut di dalam DetailCourseActivity untuk menghubungkan Activity dengan RecyclerView.
Pada kode di atas akan terjadi eror pada bagian EXTRA_COURSE_ID, klik ALT + Enter kemudian pilih Create constant field ‘EXTRA_COURSE_ID’ in ‘CourseReaderActivity’.Kotlin class DetailCourseActivity : AppCompatActivity() {
companion object {
const val EXTRA_COURSE = "extra_course"
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_detail_course)
setSupportActionBar(toolbar)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
val adapter = DetailCourseAdapter()
val extras = intent.extras
if (extras != null) {
val courseId = extras.getString(EXTRA_COURSE)
if (courseId != null) {
val modules = DataDummy.generateDummyModules(courseId)
adapter.setModules(modules)
for(course in DataDummy.generateDummyCourses()) {
if(course.courseId == courseId) {
populateCourse(course)
}
}
}
}
with(rv_module) {
isNestedScrollingEnabled = false
layoutManager = LinearLayoutManager(this@DetailCourseActivity)
setHasFixedSize(true)
this.adapter = adapter
val dividerItemDecoration = DividerItemDecoration(rv_module.context, DividerItemDecoration.VERTICAL)
addItemDecoration(dividerItemDecoration)
}
}
private fun populateCourse(courseEntity: CourseEntity) {
text_title.text = courseEntity.title
text_desc.text = courseEntity.description
text_date.text = resources.getString(R.string.deadline_date, courseEntity.deadline)
Glide.with(this)
.load(courseEntity.imagePath)
.apply(RequestOptions.placeholderOf(R.drawable.ic_loading)
.error(R.drawable.ic_error))
.into(image_poster)
btn_start.setOnClickListener {
val intent = Intent(this@DetailCourseActivity, CourseReaderActivity::class.java).apply {
putExtra(CourseReaderActivity.EXTRA_COURSE_ID, courseEntity.courseId)
}
startActivity(intent)
}
}
}Java public class DetailCourseActivity extends AppCompatActivity {
public static final String EXTRA_COURSE = "extra_course";
private Button btnStart;
private TextView textTitle;
private TextView textDesc;
private TextView textDate;
private ImageView imagePoster;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_detail_course);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
btnStart = findViewById(R.id.btn_start);
textTitle = findViewById(R.id.text_title);
textDesc = findViewById(R.id.text_description);
textDate = findViewById(R.id.text_date);
RecyclerView rvModule = findViewById(R.id.rv_module);
imagePoster = findViewById(R.id.image_poster);
DetailCourseAdapter adapter = new DetailCourseAdapter();
Bundle extras = getIntent().getExtras();
if (extras != null) {
String courseId = extras.getString(EXTRA_COURSE);
if (courseId != null) {
List<ModuleEntity> modules = DataDummy.generateDummyModules(courseId);
adapter.setModules(modules);
for (int i = 0; i < DataDummy.generateDummyCourses().size(); i++) {
CourseEntity courseEntity = DataDummy.generateDummyCourses().get(i);
if (courseEntity.getCourseId().equals(courseId)) {
populateCourse(courseEntity);
}
}
}
}
rvModule.setNestedScrollingEnabled(false);
rvModule.setLayoutManager(new LinearLayoutManager(this));
rvModule.setHasFixedSize(true);
rvModule.setAdapter(adapter);
DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(rvModule.getContext(), DividerItemDecoration.VERTICAL);
rvModule.addItemDecoration(dividerItemDecoration);
}
private void populateCourse(CourseEntity courseEntity) {
textTitle.setText(courseEntity.getTitle());
textDesc.setText(courseEntity.getDescription());
textDate.setText(getResources().getString(R.string.deadline_date, courseEntity.getDeadline()));
Glide.with(this)
.load(courseEntity.getImagePath())
.apply(RequestOptions.placeholderOf(R.drawable.ic_loading)
.error(R.drawable.ic_error))
.into(imagePoster);
btnStart.setOnClickListener(v -> {
Intent intent = new Intent(DetailCourseActivity.this, CourseReaderActivity.class);
intent.putExtra(CourseReaderActivity.EXTRA_COURSE_ID, courseEntity.getCourseId());
startActivity(intent);
});
}
}
Setelah itu masukkan “extra_course_id” sebagai value-nya.Kotlin Java
Kotlin companion object {
const val EXTRA_COURSE_ID = "extra_course_id"
}Java public static final String EXTRA_COURSE_ID = "extra_course_id";
- Selanjutnya, buatlah kembali kelas baru dan beri nama SectionsPagerAdapter di package home. Kemudian tambahkan kode berikut:
Kotlin class SectionsPagerAdapter(private val mContext: Context, fm: FragmentManager) : FragmentPagerAdapter(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
companion object {
@StringRes
private val TAB_TITLES = intArrayOf(R.string.home, R.string.bookmark)
}
override fun getItem(position: Int): Fragment =
when (position) {
0 -> AcademyFragment()
1 -> BookmarkFragment()
else -> Fragment()
}
override fun getPageTitle(position: Int): CharSequence? = mContext.resources.getString(TAB_TITLES[position])
override fun getCount(): Int = 2
}Java public class SectionsPagerAdapter extends FragmentPagerAdapter {
@StringRes
private static final int[] TAB_TITLES = new int[]{R.string.home, R.string.bookmark};
private final Context mContext;
SectionsPagerAdapter(Context context, FragmentManager fm) {
super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
mContext = context;
}
@NonNull
@Override
public Fragment getItem(int position) {
switch (position){
case 0:
return new AcademyFragment();
case 1:
return new BookmarkFragment();
default:
return new Fragment();
}
}
@Nullable
@Override
public CharSequence getPageTitle(int position) {
return mContext.getResources().getString(TAB_TITLES[position]);
}
@Override
public int getCount() {
return 2;
}
} - Bukalah HomeActivity, hubungkan AcademyFragment dan BookmarkFragment dengan HomeActivity. Tambahkan kode berikut:
Kotlin class HomeActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_home)
val sectionsPagerAdapter = SectionsPagerAdapter(this, supportFragmentManager)
view_pager.adapter = sectionsPagerAdapter
tabs.setupWithViewPager(view_pager)
supportActionBar?.elevation = 0f
}
}Java public class HomeActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
SectionsPagerAdapter sectionsPagerAdapter = new SectionsPagerAdapter(this, getSupportFragmentManager());
ViewPager viewPager = findViewById(R.id.view_pager);
viewPager.setAdapter(sectionsPagerAdapter);
TabLayout tabs = findViewById(R.id.tabs);
tabs.setupWithViewPager(viewPager);
if (getSupportActionBar() != null) {
getSupportActionBar().setElevation(0);
}
}
} - Anda sudah membuat halaman Academy, Bookmark dan Detail Course. Sampai sini Anda bisa mencobanya dahulu. Langkah selanjunya, Anda akan membuat halaman untuk list Module dan Detail Module. Sebelum itu, tambahkan sebuah kelas baru di package reader.
Setelah itu tambah kode berikut:
CourseReaderCallback nantinya akan digunakan untuk pindah dari halaman satu ke halaman lain.Kotlin interface CourseReaderCallback {
fun moveTo(position: Int, moduleId: String)
}Java public interface CourseReaderCallback {
void moveTo(int position, String moduleId);
} - Bukalah ModuleContentFragment dan ubah kode di dalamnya untuk menampilkan data dummy di WebView.
Dengan begitu, kelas ModuleContentFragment sudah bisa menampilkan data dummy di WebView.Kotlin class ModuleContentFragment : Fragment() {
companion object {
val TAG = ModuleContentFragment::class.java.simpleName
fun newInstance(): ModuleContentFragment {
return ModuleContentFragment()
}
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_module_content, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
if (activity != null) {
val content = ContentEntity("<h3 class=\\\"fr-text-bordered\\\">Contoh Content</h3><p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>")
populateWebView(content)
}
}
private fun populateWebView(content: ContentEntity) {
web_view.loadData(content.content, "text/html", "UTF-8")
}
}Java public class ModuleContentFragment extends Fragment {
public static final String TAG = ModuleContentFragment.class.getSimpleName();
private WebView webView;
public ModuleContentFragment() {
// Required empty public constructor
}
public static ModuleContentFragment newInstance() {
return new ModuleContentFragment();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_module_content, container, false);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
webView = view.findViewById(R.id.web_view);
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (getActivity() != null) {
ContentEntity content = new ContentEntity("<h3 class=\\\"fr-text-bordered\\\">Contoh Content</h3><p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>");
populateWebView(content);
}
}
private void populateWebView(ContentEntity content) {
webView.loadData(content.getContent(), "text/html", "UTF-8");
}
} - Sebelum masuk ke ModuleListFragment, buatlah kembali kelas adapter baru dan beri nama ModuleListAdapter di package reader.list.
Selanjutnya, ubahlah kode pada kelas tersebut:
Pada kelas CourseReaderAdapter terdapat sebuah interface MyAdapterClickListener yang nantinya digunakan untuk berpindah ke halaman ModuleContentFragment.Kotlin class ModuleListAdapter internal constructor(private val listener: MyAdapterClickListener) : RecyclerView.Adapter<ModuleListAdapter.ModuleViewHolder>() {
private val listModules = ArrayList<ModuleEntity>()
internal fun setModules(modules: List<ModuleEntity>?) {
if (modules == null) return
listModules.clear()
listModules.addAll(modules)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ModuleViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.items_module_list_custom, parent, false)
return ModuleViewHolder(view)
}
override fun onBindViewHolder(viewHolder: ModuleViewHolder, position: Int) {
val module = listModules[position]
viewHolder.bind(module)
viewHolder.itemView.setOnClickListener {
listener.onItemClicked(viewHolder.adapterPosition, listModules[viewHolder.adapterPosition].moduleId)
}
}
override fun getItemCount(): Int = listModules.size
inner class ModuleViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val textTitle: TextView = itemView.findViewById(R.id.text_module_title)
fun bind(module: ModuleEntity) {
textTitle.text = module.title
}
}
}
internal interface MyAdapterClickListener {
fun onItemClicked(position: Int, moduleId: String)
}Java public class ModuleListAdapter extends RecyclerView.Adapter<ModuleListAdapter.ModuleViewHolder> {
private final MyAdapterClickListener listener;
private List<ModuleEntity> listModules = new ArrayList<>();
ModuleListAdapter(MyAdapterClickListener listener) {
this.listener = listener;
}
void setModules(List<ModuleEntity> listModules) {
if (listModules == null) return;
listModules.clear();
listModules.addAll(listModules);
}
@NonNull
@Override
public ModuleViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new ModuleViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.items_module_list_custom, parent, false));
}
@Override
public void onBindViewHolder(ModuleViewHolder viewHolder, int position) {
ModuleEntity module = listModules.get(position);
viewHolder.bind(module);
viewHolder.itemView.setOnClickListener(v ->
listener.onItemClicked(viewHolder.getAdapterPosition(), listModules.get(viewHolder.getAdapterPosition()).getModuleId())
);
}
@Override
public int getItemCount() {
return listModules.size();
}
class ModuleViewHolder extends RecyclerView.ViewHolder {
final TextView textTitle;
ModuleViewHolder(View itemView) {
super(itemView);
textTitle = itemView.findViewById(R.id.text_module_title);
}
void bind(ModuleEntity module) {
textTitle.setText(module.getTitle());
}
}
}
interface MyAdapterClickListener {
void onItemClicked(int position, String moduleId);
} - Setelah Anda membuat kelas Adapter, tambahkan kode berikut di dalam ModuleListFragment untuk menghubungkan Fragment dengan RecyclerView.
Kotlin class ModuleListFragment : Fragment(), MyAdapterClickListener {
companion object {
val TAG = ModuleListFragment::class.java.simpleName
fun newInstance(): ModuleListFragment = ModuleListFragment()
}
private lateinit var adapter: ModuleListAdapter
private lateinit var courseReaderCallback: CourseReaderCallback
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_module_list, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
adapter = ModuleListAdapter(this)
populateRecyclerView(DataDummy.generateDummyModules("a14"))
}
override fun onAttach(context: Context) {
super.onAttach(context)
courseReaderCallback = context as CourseReaderActivity
}
override fun onItemClicked(position: Int, moduleId: String) {
courseReaderCallback.moveTo(position, moduleId)
}
private fun populateRecyclerView(modules: List<ModuleEntity>) {
progress_bar.visibility = View.GONE
adapter.setModules(modules)
with(rv_module) {
layoutManager = LinearLayoutManager(context)
setHasFixedSize(true)
adapter = adapter
}
val dividerItemDecoration = DividerItemDecoration(rv_module.context, DividerItemDecoration.VERTICAL)
rv_module.addItemDecoration(dividerItemDecoration)
}
}Java public class ModuleListFragment extends Fragment implements MyAdapterClickListener {
public static final String TAG = ModuleListFragment.class.getSimpleName();
private ModuleListAdapter adapter;
private CourseReaderCallback courseReaderCallback;
private RecyclerView recyclerView;
private ProgressBar progressBar;
public ModuleListFragment() {
// Required empty public constructor
}
public static ModuleListFragment newInstance() {
return new ModuleListFragment();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_module_list, container, false);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
recyclerView = view.findViewById(R.id.rv_module);
progressBar = view.findViewById(R.id.progress_bar);
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (getActivity() != null) {
adapter = new ModuleListAdapter(this);
populateRecyclerView(DataDummy.generateDummyModules("a14"));
}
}
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
courseReaderCallback = ((CourseReaderActivity) context);
}
@Override
public void onItemClicked(int position, String moduleId) {
courseReaderCallback.moveTo(position, moduleId);
}
private void populateRecyclerView(List<ModuleEntity> modules) {
progressBar.setVisibility(View.GONE);
adapter.setModules(modules);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
recyclerView.setHasFixedSize(true);
recyclerView.setAdapter(adapter);
DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(recyclerView.getContext(), DividerItemDecoration.VERTICAL);
recyclerView.addItemDecoration(dividerItemDecoration);
}
} - Ketika Anda memanggil generateDummyModules(), variable “a14” adalah sampel inputan data academy. Nanti akan kita ubah sesuai dengan posisi Fragment yang akan diklik.
Dan dengan menambahkan kode di atas, maka akan terjadi eror dibagian kode ini:
Ini terjadi karena CourseReaderActivity belum mengimplementasikan CourseReaderCallback.Kotlin courseReaderCallback = context as CourseReaderActivity
Java courseReaderCallback = ((CourseReaderActivity) context);
- Bukalah CourseReaderActivity, hubungkan ModuleContentFragment dan ModuleListFragment dengan CourseReaderActivity.
Metode moveTo digunakan untuk memanggil ModuleContentFragment sesuai dengan posisi dan moduleId yang dipilih.Kotlin class CourseReaderActivity : AppCompatActivity(), CourseReaderCallback {
companion object {
const val EXTRA_COURSE_ID = "extra_course_id"
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_course_reader)
val bundle = intent.extras
if (bundle != null) {
val courseId = bundle.getString(EXTRA_COURSE_ID)
if (courseId != null) {
populateFragment()
}
}
}
override fun moveTo(position: Int, moduleId: String) {
val fragment = ModuleContentFragment.newInstance()
supportFragmentManager.beginTransaction().add(R.id.frame_container, fragment, ModuleContentFragment.TAG)
.addToBackStack(null)
.commit()
}
override fun onBackPressed() {
if (supportFragmentManager.backStackEntryCount <= 1) {
finish()
} else {
super.onBackPressed()
}
}
private fun populateFragment() {
val fragmentTransaction = supportFragmentManager.beginTransaction()
var fragment = supportFragmentManager.findFragmentByTag(ModuleListFragment.TAG)
if (fragment == null) {
fragment = ModuleListFragment.newInstance()
fragmentTransaction.add(R.id.frame_container, fragment, ModuleListFragment.TAG)
fragmentTransaction.addToBackStack(null)
}
fragmentTransaction.commit()
}
}Java public class CourseReaderActivity extends AppCompatActivity implements CourseReaderCallback {
public static final String EXTRA_COURSE_ID = "extra_course_id";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_course_reader);
CourseReaderViewModel viewModel = new ViewModelProvider(this, new ViewModelProvider.NewInstanceFactory()).get(CourseReaderViewModel.class);
Bundle bundle = getIntent().getExtras();
if (bundle != null) {
String courseId = bundle.getString(EXTRA_COURSE_ID);
if (courseId != null) {
viewModel.setCourseId(courseId);
populateFragment();
}
}
}
@Override
public void moveTo(int position, String moduleId) {
Fragment fragment = ModuleContentFragment.newInstance();
getSupportFragmentManager().beginTransaction().add(R.id.frame_container, fragment, ModuleContentFragment.TAG)
.addToBackStack(null)
.commit();
}
@Override
public void onBackPressed() {
if (getSupportFragmentManager().getBackStackEntryCount() <= 1) {
finish();
} else {
super.onBackPressed();
}
}
private void populateFragment() {
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
Fragment fragment = getSupportFragmentManager().findFragmentByTag(ModuleListFragment.TAG);
if (fragment == null) {
fragment = ModuleListFragment.newInstance();
fragmentTransaction.add(R.id.frame_container, fragment, ModuleListFragment.TAG);
fragmentTransaction.addToBackStack(null);
}
fragmentTransaction.commit();
}
} - Inilah saat yang ditunggu-tunggu. Kita akan menjalankan aplikasi. Silakan jalankan aplikasi yang Anda buat, hasilnya akan seperti ini:
Bedah Kode
Proyek Academy ini terdiri dari 3 Activity yakni HomeActivity, DetailCourseActivity dan CourseReaderActivity. Dalam proyek ini juga mempunyai 4 Fragment yakni AcademyFragment, BookmarkFragment, ModuleListFragment dan ModuleContentFragment. Secara fungsi atau kegunaan akan jadi seperti ini:
- HomeActivity: Menampilkan 2 Fragment (AcademyFragment dan BookmarkFragment) dan sebagai halaman utama dari Aplikasi.
- DetailCourseActivity: Menampilkan detail Course dan menampilkan list Module yang ada tiap Course-nya. Selain itu di halaman ini juga nantinya akan ada tombol bookmark untuk menyimpan course yang Anda suka.
- CourseReader: Menampilkan 2 Fragment(ModuleListFragment dan ModuleContentFragment) dan di Activity ini nanti akan ada 2 tampilan yakni untuk ukuran layar yang besar dan kecil.
- AcademyFragment: Digunakan untuk menampilkan semua Course.
- BookmarkFragment: Digunakan untuk menampilkan semua Course yang sudah Anda bookmark.
- ModuleListFragment: Digunakan untuk menampilkan semua Module sesuai Course yang dipilih.
- ModuleContentFragment: Menampilkan Content dari Module yang dipilih.
Selain itu, proyek Academy nantinya akan menerapkan berbagai komponen Android Jetpack seperti ViewModel, LiveData, Room dan Pagination. Selain itu ada materi pendukung juga seperti Repository, IdleResource, UnitTesting, InstrumentalTest, dll. Tentunya materi akan dibahas secara bertahap. Yang paling keren, proyek Academy akan menggunakan prinsip Offline-Online sehingga ketika data tidak ada di local, maka akan request data dari network dan semua data akan disimpan ke local pada saat itu juga.