I have recently discovered abut Viewpager 2 and exoplayer and not sure how to integrate them both. Viewpager contains a list with video player and each takes up the full screen. You can swipe to move to the next video. Can anybody help me with a simple implementation of Exoplayer with Viewpager2
Yes I can help you.
It is a very simple implementation of viewpager2 with ExoPlayer.
It will show you online videos and you can swipe these videos horizontally.
Lets discuss it step by step:
Step 1:
Add internet permission in your Manifest file:
<uses-permission android:name="android.permission.INTERNET"/>
Step 2:
Add ExoPlayer dependency to your build.gradle
// exo player
implementation 'com.google.android.exoplayer:exoplayer:2.17.1'
Step 3 :
Add the following code to your themes.xml
<style name="ClickableView">
<item name="colorControlHighlight">#android:color/darker_gray</item>
<item name="android:background">?selectableItemBackgroundBorderless</item>
</style>
Step 4:
Create a controller layout file to control ExoPlayer named as custom_controller.xml. And then paste bellow code in that file.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:background="#80000000"
android:layout_height="match_parent">
<ImageView
android:id="#+id/exo_lock"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:padding="10dp"
android:src="#drawable/ic_baseline_lock_open"
android:theme="#style/ClickableView" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/sec_controlvid1"
android:orientation="horizontal"
android:layout_centerInParent="true">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/exo_rew"
android:src="#drawable/ic_baseline_replay"
android:layout_marginLeft="30dp"
android:theme="#style/ClickableView"/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/exo_play"
android:src="#drawable/ic_baseline_play_arrow"
android:layout_marginHorizontal="30dp"
android:theme="#style/ClickableView"/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/exo_pause"
android:src="#drawable/ic_baseline_pause"
android:layout_marginHorizontal="30dp"
android:theme="#style/ClickableView"/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/exo_ffwd"
android:layout_marginRight="30dp"
android:src="#drawable/ic_baseline_forward"
android:theme="#style/ClickableView"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/sec_controlvid2"
android:orientation="vertical"
android:padding="8dp"
android:gravity="center"
android:layout_alignParentBottom="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="8dp"
android:gravity="center"
android:layout_alignParentBottom="true">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/exo_position"
android:textColor="#color/white"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="/"
android:layout_marginHorizontal="4dp"
android:textColor="#CBCDC8"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/exo_duration"
android:layout_weight="1"
android:textColor="#CBCDC8"/>
<ImageView
android:layout_width="30dp"
android:layout_height="30dp"
android:src="#drawable/ic_baseline_fullscreen"
android:id="#+id/bt_fullscreen"
android:theme="#style/ClickableView"/>
</LinearLayout>
<com.google.android.exoplayer2.ui.DefaultTimeBar
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/exo_progress"
app:unplayed_color="#42454E"
app:buffered_color="#95989F"
app:played_color="#FF0000"
app:scrubber_color="#FF0000"
android:layout_alignParentBottom="true"
android:layout_marginTop="-8dp"/>
</LinearLayout>
</RelativeLayout>
Step 5:
Create a layout file for representing items in viewpager named as video_card_item.xml and write the following code in it:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:gravity="center_vertical"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.exoplayer2.ui.PlayerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerInParent="true"
android:id="#+id/statusSliderVideo"
app:use_controller="true"
android:background="#android:color/background_dark"
app:player_layout_id="#layout/exo_player_view"
app:controller_layout_id="#layout/custom_controller"/>
<ImageView
android:id="#+id/statusSliderThumbnailImage"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerInParent="true"
android:padding="10dp"
android:scaleType="fitCenter"
android:src="#drawable/ic_default_image" />
<ImageView
android:id="#+id/playPauseBtn"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_centerInParent="true"
android:src="#drawable/ic_play"
app:tint="#color/white" />
<ProgressBar
android:layout_width="80dp"
android:layout_height="80dp"
android:visibility="gone"
android:id="#+id/progress_bar"
android:layout_centerInParent="true"/>
</RelativeLayout>
Step 6 :
Create adapter class for viewpager items named as VideoSliderAdapter.java and write bellow code in it:
package com.example.practiceproject;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.media.ThumbnailUtils;
import android.net.Uri;
import android.provider.MediaStore;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.exoplayer2.MediaItem;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.ui.PlayerView;
import java.util.ArrayList;
public class VideoSliderAdapter extends RecyclerView.Adapter<VideoSliderAdapter.ViewHolder> {
private Context context;
private ArrayList<String> pathsList;
private AppCompatActivity activity;
private boolean isFullScreen = false;
public static boolean isFullLock = false;
public VideoSliderAdapter(Context context, ArrayList<String> pathsList, AppCompatActivity activity) {
this.context = context;
this.pathsList = pathsList;
this.activity = activity;
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.video_card_item, parent, false);
return new ViewHolder(view);
}
#SuppressLint("ClickableViewAccessibility")
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
holder.thumbnailImage.setImageBitmap(ThumbnailUtils.createVideoThumbnail(pathsList.get(position),
MediaStore.Video.Thumbnails.FULL_SCREEN_KIND));
// get data
Uri videoUri = Uri.parse(pathsList.get(position));
holder.bt_fullscreen.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (!isFullScreen) {
holder.bt_fullscreen.setImageDrawable(
ContextCompat.getDrawable(context, R.drawable.ic_baseline_fullscreen_exit)
);
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
} else {
holder.bt_fullscreen.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.ic_baseline_fullscreen));
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
isFullScreen = !isFullScreen;
}
});
holder.bt_lockscreen.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (isFullLock) {
holder.bt_lockscreen.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.ic_baseline_lock_open));
} else {
holder.bt_lockscreen.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.ic_baseline_lock));
}
isFullLock = !isFullLock;
lockScreen(isFullLock, holder.sec_mid, holder.sec_bottom);
}
});
holder.playPauseBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
holder.playPauseBtn.setVisibility(View.GONE);
// holder.thumbnailImage.setVisibility(View.GONE);
holder.playerView.setVisibility(View.VISIBLE);
holder.simpleExoPlayer = new SimpleExoPlayer.Builder(context)
.setSeekBackIncrementMs(5000)
.setSeekForwardIncrementMs(5000)
.build();
holder.playerView.setPlayer(holder.simpleExoPlayer);
holder.playerView.setKeepScreenOn(true);
holder.simpleExoPlayer.addListener(new Player.Listener() {
#Override
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
if (playbackState == Player.STATE_BUFFERING) {
holder.progressBar.setVisibility(View.VISIBLE);
} else if (playbackState == Player.STATE_READY) {
holder.progressBar.setVisibility(View.GONE);
}
}
});
MediaItem mediaItem = MediaItem.fromUri(videoUri);
holder.simpleExoPlayer.setMediaItem(mediaItem);
holder.simpleExoPlayer.prepare();
holder.simpleExoPlayer.play();
}
});
}
private void lockScreen(boolean isFullLock, LinearLayout sec_mid, LinearLayout sec_bottom) {
if (isFullLock) {
sec_mid.setVisibility(View.INVISIBLE);
sec_bottom.setVisibility(View.INVISIBLE);
} else {
sec_mid.setVisibility(View.VISIBLE);
sec_bottom.setVisibility(View.VISIBLE);
}
}
#Override
public int getItemCount() {
return pathsList.size();
}
public static class ViewHolder extends RecyclerView.ViewHolder {
PlayerView playerView;
ImageView thumbnailImage;
ImageView playPauseBtn;
ImageView bt_fullscreen, bt_lockscreen;
SimpleExoPlayer simpleExoPlayer;
ProgressBar progressBar;
LinearLayout sec_mid, sec_bottom;
public ViewHolder(#NonNull View view) {
super(view);
playerView = view.findViewById(R.id.statusSliderVideo);
thumbnailImage = view.findViewById(R.id.statusSliderThumbnailImage);
playPauseBtn = view.findViewById(R.id.playPauseBtn);
progressBar = view.findViewById(R.id.progress_bar);
bt_fullscreen = view.findViewById(R.id.bt_fullscreen);
bt_lockscreen = view.findViewById(R.id.exo_lock);
sec_mid = view.findViewById(R.id.sec_controlvid1);
sec_bottom = view.findViewById(R.id.sec_controlvid2);
}
}
}
Step 7 :
Paste following code in your activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:orientation="vertical"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.viewpager2.widget.ViewPager2
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:clipToPadding="true"
android:foregroundGravity="center"
android:overScrollMode="never"
android:id="#+id/videoViewPager"/>
</LinearLayout>
Step 8 :
Paste bellow code in your MainActivity.java:
package com.example.practiceproject;
import androidx.appcompat.app.AppCompatActivity;
import androidx.viewpager2.widget.ViewPager2;
import android.os.Bundle;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
private ViewPager2 viewPager;
private VideoSliderAdapter myAdapter;
ArrayList<String> videoPaths;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
viewPager = findViewById(R.id.videoViewPager);
videoPaths = new ArrayList<>();
videoPaths.add("http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDream.mp4");
videoPaths.add("http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerBlazes.mp4");
videoPaths.add("http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerEscapes.mp4");
videoPaths.add("http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerFun.mp4");
videoPaths.add("http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerJoyrides.mp4");
// add paths for video simllarly
myAdapter = new VideoSliderAdapter(getApplicationContext(), videoPaths,
MainActivity.this);
viewPager.setAdapter(myAdapter);
viewPager.setPadding(10, 0, 10, 0);
}
}
Completed
Now you are good to go. Enjoy the app.. Best of Luck.
If you want to get project files (Source Code) for more simplicity, you can get source code from my github profile.
link : https://github.com/AbdullahProgrammer426351/Custom-Video-Slider-with-Exo-Player-and-ViewPager2
Related
I just started working with Android Studio, and I've been trying to figure out what's wrong with this code for a few days now. When I run the program it shows nothing, and also there is no errors from Android Studio. I read the posts on the subject, but unfortunately I found nothing appropriate.
This is my activity class.
package ie.wit.donationx.activities
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.firebase.database.*
import ie.wit.donationx.adapters.EventsAdapter
import ie.wit.donationx.databinding.ActivityEventsFeedBinding
import ie.wit.donationx.models.EventData
class EventsFeed : AppCompatActivity() {
lateinit var mDataBase: DatabaseReference
private lateinit var eventList:ArrayList<EventData>
private lateinit var mAdapter: EventsAdapter
private lateinit var binding: ActivityEventsFeedBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//setContentView(R.layout.activity_events_feed)
binding = ActivityEventsFeedBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
eventList = ArrayList()
mAdapter = EventsAdapter(this,eventList)
var linearLayoutManager = LinearLayoutManager(this)
binding.recyclerEvents.layoutManager = linearLayoutManager
binding.recyclerEvents.setHasFixedSize(true)
binding.recyclerEvents.adapter = mAdapter
getEventsData()
}
private fun getEventsData() {
mDataBase = FirebaseDatabase.getInstance().getReference("Events")
mDataBase.addValueEventListener(object : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
if (snapshot.exists()) {
for (eventSnapshot in snapshot.children) {
val event = eventSnapshot.getValue(EventData::class.java)
eventList.add(event!!)
}
binding.recyclerEvents.adapter = mAdapter
}
}
override fun onCancelled(error: DatabaseError) {
TODO("Not yet implemented")
}
})
}
}
This is my Adapter
package ie.wit.donationx.adapters
import android.content.Context
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import androidx.recyclerview.widget.RecyclerView
import ie.wit.donationx.R
import ie.wit.donationx.databinding.ItemListBinding
import ie.wit.donationx.models.EventData
class EventsAdapter(
var c: Context, var eventList: ArrayList<EventData>)
: RecyclerView.Adapter<EventsAdapter.EventViewHolder>() {
inner class EventViewHolder(var v: ItemListBinding): RecyclerView.ViewHolder(v.root){}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): EventViewHolder {
val infilter = LayoutInflater.from(parent.context)
val v = DataBindingUtil.inflate<ItemListBinding>(
infilter,R.layout.item_list,parent,
false
)
return EventViewHolder(v)
}
override fun onBindViewHolder(holder: EventViewHolder, position: Int) {
val newList = eventList[position]
holder.v.isEvent = eventList[position]
holder.adapterPosition
}
override fun getItemCount(): Int {
return eventList.size
}
}
XML recyclerView
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".activities.EventsFeed">
<com.google.android.material.imageview.ShapeableImageView
android:id="#+id/main_shapeable"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_marginStart="10dp"
android:layout_marginTop="5dp"
android:layout_marginEnd="10dp"
android:background="#color/white"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="#+id/main_search"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp"
android:ems="10"
android:hint="search"
android:inputType="textPersonName"
android:textSize="14sp"
app:layout_constraintBottom_toBottomOf="#+id/main_shapeable"
app:layout_constraintEnd_toEndOf="#+id/main_shapeable"
app:layout_constraintStart_toStartOf="#+id/main_shapeable"
app:layout_constraintTop_toTopOf="#+id/main_shapeable" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recyclerEvents"
tools:listitem="#layout/item_list"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="5dp"
android:layout_marginBottom="10dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/main_shapeable"/>
</androidx.constraintlayout.widget.ConstraintLayout>
XML file for items in recyclerView
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="isEvent"
type="ie.wit.donationx.models.EventData" />
</data>
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardCornerRadius="5dp"
>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:orientation="horizontal"
android:layout_margin="10dp"
android:padding="8dp"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="#+id/eventImg"
android:imageUrl="#{isEvent.image}"
android:scaleType="centerCrop"
android:layout_width="140dp"
android:layout_height="140dp"/>
<LinearLayout
android:orientation="vertical"
android:gravity="center"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/eventTitle"
android:textColor="#color/black"
android:textSize="20sp"
android:gravity="center"
android:textStyle="bold|normal"
android:layout_gravity="center"
android:text="#{isEvent.evenTitle}"
android:layout_margin="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<TextView
android:id="#+id/eventInfo"
android:textColor="#android:color/darker_gray"
android:textSize="15sp"
android:gravity="center"
android:textStyle="bold|normal"
android:layout_gravity="center"
android:text="#{isEvent.info}"
android:layout_margin="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
</LinearLayout>
<View
android:background="#android:color/darker_gray"
android:layout_width="match_parent"
android:layout_height="1dp"/>
</LinearLayout>
</androidx.cardview.widget.CardView>
</layout>
Data file
package ie.wit.donationx.models
class EventData {
var evenTitle: String? = null
var info: String? = null
var image: String? = null
constructor(){}
constructor(
evenTitle:String?,
info:String?,
image:String?
){
this.evenTitle = evenTitle
this.info = info
this.image = image
}
}
Firebase DB structure
When adding new list to the RecyclerView Adapter you need to call NotifyDataSetChanged()
Also, i advise to remove the list from the constructor of the adapter, and make a public function inside the adapter class that will update the private list created in the class.
private var items: MutableList<EventData> = mutableListOf()
fun setItems(newItemsList : List<EventData>){
eventList.clear()
eventList.addAll(newItemsList)
notifyDataSetChanged()
}
If you only update part of the list, you can create a separate function for that and use the more efficient notifyItemRangeInserted(), notifyItemInserted() or notifyItemChanged() \ notifyItemRangeChanged
You don't need to setAdaper again after getting EventsData
Just replace this line in getEventsData() :
binding.recyclerEvents.adapter = mAdapter
With :
if (eventList.size > 0) {
mAdapter.notifyDataSetChanged()
}
Edit:
You should not to add
android:imageUrl="#{isEvent.image}" in the ImageView,
you can use library to download the images from URL Like:
Picasso.
or
Fresco.
For Example using Picasso in your adapter:
override fun onBindViewHolder(holder: EventViewHolder, position: Int) {
val newList = eventList[position]
holder.v.isEvent = eventList[position]
holder.adapterPosition
// here to download image for URL
Picasso.get().load(eventList[position].image)
.into(holder.v.eventImg)
}
Don't forget to add dependencies
implementation 'com.squareup.picasso:picasso:2.8'
I am currently working on a mobile application in my uni classes that utilizes a seekBar to let the user decide to use a timer for a quiz. This application uses main to host all of the fragments. currently I just want the textbox to display where the user scrolled the seek bar to but am struggling to find a solution. any advice would be greatly appreciated. this is the code I have within the
TitleFragment.kt:
package com.example.android.guesstheword.screens.title
import android.os.Bundle
import android.view.DragEvent
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.SeekBar
import android.widget.SeekBar.OnSeekBarChangeListener
import android.widget.Switch
import android.widget.TextView
import android.widget.Toast
import androidx.core.view.isVisible
import androidx.databinding.DataBindingUtil
import androidx.databinding.adapters.SeekBarBindingAdapter
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController
import com.example.android.guesstheword.R
import com.example.android.guesstheword.databinding.TitleFragmentBinding
import kotlinx.android.synthetic.main.title_fragment.*
/**
* Fragment for the starting or title screen of the app
*/
class TitleFragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View {
// Inflate the layout for this fragment
val binding: TitleFragmentBinding = DataBindingUtil.inflate(
inflater, R.layout.title_fragment, container, false)
binding.timerSwitch.setOnClickListener {
if(binding.timerSwitch.isChecked){
binding.timerBar.visibility = View.VISIBLE
}
else{
binding.timerBar.visibility = View.INVISIBLE
}
}
val seekBar = binding.timerBar
seekBar.setOnSeekBarChangeListener(binding)
binding.playGameButton.setOnClickListener {
findNavController().navigate(TitleFragmentDirections.actionTitleToGame())
}
return binding.root
}
}
private fun SeekBar.setOnSeekBarChangeListener(binding: TitleFragmentBinding) {
val seconds = binding.timerSeconds
seconds.visibility = View.VISIBLE
seconds.text = binding.timerBar.progress.toString()
}
title_fragment.xml:
<?xml version="1.0" encoding="utf-8"?><!--
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/title_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".screens.title.TitleFragment">
<TextView
android:id="#+id/get_ready_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:fontFamily="sans-serif"
android:text="#string/get_ready"
android:textColor="#color/black_text_color"
android:textSize="14sp"
android:textStyle="normal"
app:layout_constraintBottom_toTopOf="#+id/title_text"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed" />
<TextView
android:id="#+id/title_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:fontFamily="sans-serif"
android:text="#string/title_text"
android:textColor="#color/black_text_color"
android:textSize="34sp"
android:textStyle="normal"
app:layout_constraintBottom_toTopOf="#+id/play_game_button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/get_ready_text"
app:layout_constraintVertical_chainStyle="packed" />
<Button
android:id="#+id/play_game_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="32dp"
android:text="#string/play_button"
android:theme="#style/GoButton"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<Switch
android:id="#+id/timer_switch"
android:layout_width="95dp"
android:layout_height="52dp"
android:text="Timer"
android:min="5"
android:max="360"
app:layout_constraintBottom_toTopOf="#+id/play_game_button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/title_text"
app:layout_constraintVertical_bias="0.064"/>
<SeekBar
android:id="#+id/timerBar"
android:layout_width="132dp"
android:layout_height="85dp"
android:clickable="false"
android:visibility="gone"
app:layout_constraintBottom_toTopOf="#+id/play_game_button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/timer_switch"
app:layout_constraintVertical_bias="0.144" />
<TextView
android:id="#+id/timer_seconds"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
app:layout_constraintBottom_toTopOf="#+id/play_game_button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/timerBar"
app:layout_constraintVertical_bias="0.17" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
You created an extension function called setOnSeekBarChangeListener which does not actually set it.
In order to set the listener, you need to do something like this:
binding.timerBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener{
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
// here, you react to the value being set in seekBar
}
override fun onStartTrackingTouch(seekBar: SeekBar) {
// you can probably leave this empty
}
override fun onStopTrackingTouch(seekBar: SeekBar) {
// you can probably leave this empty
}
})
But keep in mind that this is NOT the extension function you created.
i discovered in android studio setText of TextView doesn't work. see next code. I expect if in nextCode append button is clicked, in textView text is changed to editText.text.toString(). but doesn't be changed. why so?
Activity.kt :
package com.example.myapplication
import android.content.Context
import android.content.Context.LAYOUT_INFLATER_SERVICE
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.AttributeSet
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.widget.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val editText = findViewById(R.id.editText) as EditText
val textView = findViewById(R.id.textView) as TextView
val button = findViewById(R.id.button) as Button
button.setOnClickListener(View.OnClickListener(){
#Override
fun onClick(view : View){
textView.setText(editText.text.toString())
}
})
}
}
activity_main.xml :
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:orientation="vertical"
android:id="#+id/root_layout">
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/editText"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="append"
android:id="#+id/button"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/textView"
android:text="4"/>
</LinearLayout>
You are not setting up the click listener properly. Either specify
button.setOnClickListener(object : View.OnClickListener {
override fun onClick(view: View) {
textView.setText(editText.text.toString())
}
})
or the lambda construction:
button.setOnClickListener {
textView.setText(editText.text.toString())
}
I have four radio button inside the radio group in recyclerview. when i click first position of radio button it reflect on eighth position of radio button. How can i resolve this? If no radio button clicked the group will disable. how can i do this also? when i click the submit button calculate how many radio button clicked?
Xml :
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="#+id/txt_question"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:maxLines="5"
android:text="#string/question_1"
android:textColor="#000"
android:textSize="16sp"
android:textStyle="bold" />
<RadioGroup
android:id="#+id/radio_group"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="15dp">
<RadioButton
android:id="#+id/radio_option_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="3" />
<RadioButton
android:id="#+id/radio_option_2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="3" />
<RadioButton
android:id="#+id/radio_option_3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="3" />
<RadioButton
android:id="#+id/radio_option_4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="3" />
</RadioGroup>
</LinearLayout>
</android.support.v7.widget.CardView>
Adapter :
public class TestAdapter extends RecyclerView.Adapter<TestAdapter.RecyclerVH> {
private Context mCtx;
private List<TestResult> testList;
public TestAdapter(Context mCtx, List<TestResult> testList) {
this.mCtx = mCtx;
this.testList = testList;
}
#NonNull
#Override
public RecyclerVH onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
LayoutInflater layoutInflater = LayoutInflater.from(mCtx);
View view = layoutInflater.inflate(R.layout.list_test, parent, false);
return new RecyclerVH(view);
}
#Override
public void onBindViewHolder(#NonNull final RecyclerVH holder, final int position) {
TestResult test = testList.get(position);
String question = position + 1 + " " + test.getQuestion();
holder.question.setText(question);
holder.option_1.setText(test.getChoice0());
holder.option_2.setText(test.getChoice1());
holder.option_3.setText(test.getChoice2());
holder.option_4.setText(test.getChoice3());
// Log.d("Test",position+"");
}
#Override
public int getItemCount() {
return testList.size();
}
public void setList(ArrayList<TestResult> list) {
this.testList = list;
}
class RecyclerVH extends RecyclerView.ViewHolder {
TextView question;
RadioButton option_1;
RadioButton option_2;
RadioButton option_3;
RadioButton option_4;
RadioGroup group;
RecyclerVH(#NonNull View itemView) {
super(itemView);
question = itemView.findViewById(R.id.txt_question);
option_1 = itemView.findViewById(R.id.radio_option_1);
option_2 = itemView.findViewById(R.id.radio_option_2);
option_3 = itemView.findViewById(R.id.radio_option_3);
option_4 = itemView.findViewById(R.id.radio_option_4);
group = itemView.findViewById(R.id.radio_group);
}
}
}
add this in your adapter, it will handle item according to respective position.
#Override
public int getItemViewType(int position) {
return position;
}
I am new to Android Studio - Kotlin, I am building an app with numbers and math operators with decimal point(.dot button)In my math calculator app, if press the dot button, the button should be disabled or not entered for the second time. I want to disable or stop printing dot button(.) if pressed more than once.
Here is my code
Acitivity.kt file
package com.tripbegins.calculator
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import android.widget.Button
import com.tripbegins.calculator.R.id.*
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
var emptyText = true
fun numberEvents(view: View) {
var checkButton:Boolean = false
if(emptyText){
viewResult.setText("")
}
emptyText = false
var button = view as Button
var isClicked = viewResult.text.toString()
when (button.id) {
buttonOne.id -> isClicked += "1"
buttonTwo.id -> isClicked += "2"
buttonThree.id -> isClicked += "3"
dotButton.id-> isClicked+="."
}
viewResult.setText(isClicked)
}
var operation ="+"
var oldValues:String? = null
fun mathOperation(view: View){
var mathButton = view as Button
var isClicked = viewResult.text.toString()
when(mathButton.id){
plusButton.id-> {
operation = "+"
}
minusButton.id->{
operation="-"
}
mulButton.id->{
operation="*"
}
}
oldValues = viewResult.text.toString()
emptyText = true
}
fun Calculate(view: View){
var newValues = viewResult.text.toString()
var calulateButton:Double? = null
when(operation){
"+"-> { calulateButton = oldValues!!.toDouble() + newValues.toDouble() }
"-"-> { calulateButton = oldValues!!.toDouble() - newValues.toDouble() }
"*"-> { calulateButton = oldValues!!.toDouble() * newValues.toDouble() }
}
viewResult.setText(calulateButton.toString())
emptyText=true
}
fun clearFunction(view: View){
viewResult.setText("")
}
}
activity.xml file
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.tripbegins.calculator.MainActivity">
<EditText
android:id="#+id/viewResult"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="42dp"
android:ems="10"
android:gravity="bottom|right"
android:inputType="textPersonName"
android:text="0"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="#+id/buttonTwo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="76dp"
android:onClick="numberEvents"
android:text="2"
app:layout_constraintStart_toEndOf="#+id/buttonOne"
app:layout_constraintTop_toBottomOf="#+id/viewResult" />
<Button
android:id="#+id/plusButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="38dp"
android:onClick="mathOperation"
android:text="+"
app:layout_constraintBaseline_toBaselineOf="#+id/minusButton"
app:layout_constraintStart_toStartOf="parent" />
<Button
android:id="#+id/minusButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginTop="33dp"
android:onClick="mathOperation"
android:text="-"
app:layout_constraintStart_toEndOf="#+id/plusButton"
app:layout_constraintTop_toBottomOf="#+id/buttonTwo" />
<Button
android:id="#+id/mulButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="26dp"
android:onClick="mathOperation"
android:text="*"
app:layout_constraintBaseline_toBaselineOf="#+id/minusButton"
app:layout_constraintStart_toEndOf="#+id/minusButton" />
<Button
android:id="#+id/calButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="44dp"
android:onClick="Calculate"
android:text="Calculate"
app:layout_constraintStart_toStartOf="#+id/minusButton"
app:layout_constraintTop_toBottomOf="#+id/minusButton" />
<Button
android:id="#+id/buttonThree"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="26dp"
android:onClick="numberEvents"
android:text="3"
app:layout_constraintBaseline_toBaselineOf="#+id/buttonTwo"
app:layout_constraintStart_toEndOf="#+id/buttonTwo" />
<Button
android:id="#+id/buttonOne"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="34dp"
android:onClick="numberEvents"
android:text="1"
app:layout_constraintBaseline_toBaselineOf="#+id/buttonTwo"
app:layout_constraintStart_toStartOf="parent" />
<Button
android:id="#+id/dotButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="22dp"
android:onClick="numberEvents"
android:text="."
app:layout_constraintBaseline_toBaselineOf="#+id/calButton"
app:layout_constraintEnd_toStartOf="#+id/calButton" />
<Button
android:id="#+id/clearButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="11dp"
android:onClick="clearFunction"
android:text="Clear"
app:layout_constraintBaseline_toBaselineOf="#+id/calButton"
app:layout_constraintStart_toEndOf="#+id/calButton" />
</android.support.constraint.ConstraintLayout>
My expectation of the result
Valid entry -> 3. (.dot button should not be entered for the second time)
Invalid entry -> 3... (currently i can able to enter multiple .dot buttons)
Dot button should not pressed twice. It should be active only once.
Thanks
Mohammed
so you want to disable the button that has the name dotButton after the user clicks it for the first time :
to disable a button, use:
dotButton.isClickable=false
if you want the button to become clickable again(like when the user deletes the dot that he already pressed) you can use:
dotButton.isClickable=true
Edit:
modify your activity code like that :
when (button.id) {
buttonOne.id -> isClicked += "1"
buttonTwo.id -> isClicked += "2"
buttonThree.id -> isClicked += "3"
dotButton.id-> {
isClicked+="."
dotButton.isClickable=false//**add this line here**
}
}
fun clearFunction(view: View){
viewResult.setText("")
dotButton.isClickable=true//**add this line here**
}