I'm trying to figure out the most efficient way to structure this problem..
I'd like to click on the 'EDIT' icon in the dashboard of the MainFragment, display a DialogFragment, allow user to select/deselect up to 5 icons, save the selection, close the DialogFragment, and update the MainFragment.
Should I use MutableLiveData/Observer from a ViewModel? Or is there a better approach? I currently cannot figure out how to use the ViewModel approach correctly...
So far, this is the code I have:
MainFragment: https://i.stack.imgur.com/5fRt2.png
DialogFragment: https://i.stack.imgur.com/ZvW3d.png
ViewModel Class:
class IconDashboardViewModel() : ViewModel(){
var liveDataDashIcons: MutableLiveData<MutableList<String>> = MutableLiveData()
var liveItemData: MutableLiveData<String> = MutableLiveData()
// Observer for live list
fun getLiveDataObserver(): MutableLiveData<MutableList<String>> {
return liveDataDashIcons
}
// Observer for each icon
fun getLiveItemObserver(): MutableLiveData<String> {
return liveItemData
}
// Set icon list
fun setLiveDashIconsList(iconList: MutableLiveData<MutableList<String>>) {
liveDataDashIcons.value = iconList.value
}
// Set data for data
fun setItemData(icon : MutableLiveData<String>) {
liveItemData.value = icon.toString()
}
var iconList = mutableListOf<String>()
}
MainFragment:
private fun populateIconList() : MutableLiveData<MutableList> {
var iconList = viewModel.liveDataDashIcons
// Roster icon
if (roster_dash_layout.visibility == View.VISIBLE) {
iconList.value!!.add(getString(R.string.roster))
} else {
if (iconList.value!!.contains(getString(R.string.roster))) {
iconList.value!!.remove(getString(R.string.roster))
}
}
}
DialogFragment:
private fun setIconList(iconList: MutableList){
var iconList = viewModel.iconList
Log.d(TAG, "viewModel iconList = " + iconList)
if (iconList.contains(getString(R.string.roster))) {
binding.radioButtonRosterPick.setBackgroundResource(R.drawable.icon_helmet_blue_bg)
}
}
Related
I have a snackbar and I showing information to users. If user click Snackbar, I want to open an other fragment.
private fun showLastUnsuccessfulLoginMessage(message: String) {
val snackbar =
Snackbar.make(view!!, message , Snackbar.LENGTH_INDEFINITE)
snackbar.view.setSafeOnClickListener {
Log.wtf("clicked","setSafeOnClickListenerClicked")
}
snackbar.view.z=200f
snackbar.view.translationZ=200f
snackbar.view.setBackgroundColor(ContextCompat.getColor(activity!!,R.color.colorWhite))
val snackbarTextView: TextView = snackbar.view.findViewById(R.id.snackbar_text)
snackbarTextView.setTextColor(ContextCompat.getColor(activity!!,R.color.colorBlack))
snackbarTextView.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_warning_red, 0, 0, 0)
snackbarTextView.compoundDrawablePadding = resources.getDimensionPixelOffset(R.dimen.space_s)
val params =
snackbar.view.layoutParams
(params as FrameLayout.LayoutParams).gravity = Gravity.TOP
snackbar.view.layoutParams = params
snackbar.view.visibility = View.INVISIBLE
snackbar.addCallback(object : Snackbar.Callback() {
override fun onShown(snackbar: Snackbar?) {
super.onShown(snackbar)
animateViewFromTop(snackbar!!.view)
}
})
snackbar.show()
isShowedUnsuccessfulLoginMessage = true
object : CountDownTimer(8000, 1000) {
override fun onTick(millisUntilFinished: Long) {}
override fun onFinish() {
animateViewToTop(snackbar.view)
}
}.start()
}
With this code, Snackbar is showing and when I click the snackbar, nothing happened.
snackbar.view.setSafeOnClickListener {
Log.wtf("clicked","setSafeOnClickListenerClicked")
}
This is not working fine.
i'm a beginner in android & kotlin and i'm having an issue i been trying to figure out all day...
I have an app that fetches data from NewsApi and displays it in a recycler view , i am using Retrofit library and Room (to save favorite articles) with MVVM architecture. I want to add an option so that the user can select the country of the news from a dialog that pops up by clicking on a icon on the toolbar menu.
I have created a custom DialogFragment and have it show up, the dialog contains a spinner with a list of countries and i'm using FragmentResult and FragmentResultListener to pass the country value between dialog fragment and news fragment.
DialogFragment
class CountrySelectDialog : DialogFragment(R.layout.country_selection_dialog) {
private lateinit var binding: CountrySelectionDialogBinding
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding = CountrySelectionDialogBinding.bind(view)
binding.spCountrySelection.onItemSelectedListener =
object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(
adapterView: AdapterView<*>?,
view: View?,
position: Int,
id: Long
) {
Toast.makeText(
context,
"you selected ${adapterView?.getItemAtPosition(position).toString()}",
Toast.LENGTH_SHORT
).show()
}
override fun onNothingSelected(adapterView: AdapterView<*>?) {
}
}
binding.btnCancel.setOnClickListener {
this.dismiss()
}
binding.btnConfirm.setOnClickListener {
val result = binding.spCountrySelection.selectedItem.toString()
setFragmentResult("countryCode", bundleOf("bundleKey" to result))
this.dismiss()
}
}
}
The news Fragment is observing data from the View Model
class BreakingNewsFragment : Fragment(R.layout.fragment_breaking_news) {
lateinit var viewModel: NewsViewModel
lateinit var newsAdapter: NewsAdapter
private lateinit var binding: FragmentBreakingNewsBinding
val TAG = "BreakingNewsFragment"
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding = FragmentBreakingNewsBinding.bind(view)
viewModel = (activity as NewsActivity).viewModel
setUpRecyclerView()
setFragmentResultListener("countryCode") { countryCode, bundle ->
val result = bundle.getString("countryCode")
viewModel.countryCode = result!!}
viewModel.breakingNews.observe(viewLifecycleOwner, Observer {
when (it) {
is Resource.Success -> {
hideProgressBar()
it.data?.let {
newsAdapter.differ.submitList(it.articles.toList())
val totalPages = it.totalResults / QUERY_PAGE_SIZE + 2
isLastPage = viewModel.breakingNewsPage == totalPages
}
}
is Resource.Error -> {
hideProgressBar()
it.message?.let {
Log.e(TAG, "An error occurred: $it")
}
}
is Resource.Loading -> {
showProgressBar()
}
}
})
newsAdapter.setOnItemClickListener {
val bundle = Bundle().apply {
putSerializable("article", it)
}
findNavController().navigate(
R.id.action_breakingNewsFragment_to_articleFragment, bundle
)
}
}
ViewModel:
class NewsViewModel(val newsRepository: NewsRepository, val app: Application) : AndroidViewModel(app) {
val breakingNews: MutableLiveData<Resource<NewsResponse>> = MutableLiveData()
var breakingNewsPage = 1
var breakingNewsResponse: NewsResponse? = null
val searchNews: MutableLiveData<Resource<NewsResponse>> = MutableLiveData()
var searchNewsPage = 1
var searchNewsResponse: NewsResponse? = null
var countryCode :String = "it"
init {
getBreakingNews(countryCode)
}
fun getBreakingNews(countryCode: String) {
viewModelScope.launch {
breakingNews.postValue(Resource.Loading())
val response = newsRepository.getBreakingNews(countryCode, breakingNewsPage)
breakingNews.postValue(handleBreakingNewsResponse(response))
}
}
fun handleBreakingNewsResponse(response: Response<NewsResponse>): Resource<NewsResponse> {
if (response.isSuccessful) {
response.body()?.let { resultResponse ->
breakingNewsPage++
if (breakingNewsResponse == null) {
breakingNewsResponse = resultResponse
} else {
val oldArticles = breakingNewsResponse?.articles
val newArticles = resultResponse.articles
oldArticles?.addAll(newArticles)
}
return Resource.Success(breakingNewsResponse ?: resultResponse)
}
}
return Resource.Error(response.message())
}
fun searchNews(searchQuery: String) {
viewModelScope.launch {
searchNews.postValue(Resource.Loading())
val response = newsRepository.searchNews(searchQuery, searchNewsPage)
searchNews.postValue((handleSearchNewsResponse(response)))
}
}
fun handleSearchNewsResponse(response: Response<NewsResponse>): Resource<NewsResponse> {
if (response.isSuccessful) {
response.body()?.let { resultResponse ->
searchNewsPage++
if (searchNewsResponse == null) {
searchNewsResponse = resultResponse
} else {
val oldArticles = searchNewsResponse?.articles
val newArticles = resultResponse.articles
oldArticles?.addAll(newArticles)
}
return Resource.Success(searchNewsResponse ?: resultResponse)
}
}
return Resource.Error(response.message())
}
}
When i click on the icon on the toolbar menu the dialog appears and works fine but i can't seem to find a way to have the recycler view update with new data using given value for country
I searched everywhere and couldn't find a solution (or probably didn't understand it :S) can someone guide me into the right direction? I'm so lost...
When I click on the icon on the toolbar menu the dialog appears and works fine but I can't seem to find a way to have the recycler view update with new data using given value for country.
I am trying to pass live events from a Broadcast Receiver to the title of my Homepage.
I am passing a String from the Broadcast Receiver to my Repository successfully, but in the end my title is always null. What am I missing?
My Repository looks like this:
object Repository {
fun getAndSendData (query: String): String{
return query
}
}
Then in my ViewModel I have:
private val _data = MutableLiveData<String>()
val repoData = _data.switchMap {
liveData {
emit(Repository.getAndSendData(it))
}
}
And finally in my Composable I have:
val repoData = viewModel.repoData.observeAsState()
topBar = {
TopAppBar(
title = { Text(text = if (repoData.value == null)"null" else repoData.value!!, style = typography.body1) },
navigationIcon = {
IconButton(onClick = { scaffoldState.drawerState.open() }) {
Icon(Icons.Rounded.Menu)
}
}
)
},
I don't think we can use Live Data from the compose(jetpack) because it can run from the Main thread. I used onCommit() {} with interface from the compose.
onCommit() {
viewModel.testCountriesList(object : NetworkCallBack {
override fun test(response: GeneralResponse) {
if(response.code == 200) {
counties = response.response
responseState.value = true
} else {
responseState.value = false
}
}
})
}
I want to hide app icon from grid view and save it.
I can save application info to shared preferences as mutable set string and get it but cant convert string to application info and show in my grid view
my app activity
private var applist: List<ApplicationInfo>? = null
private var listadaptor: ApplicationAdapter? = null
private var grid: GridView? = null
private var mSelected: ArrayList<Any> = ArrayList()
var context: Activity = this
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_apps)
val packageManager = getPackageManager()
LoadApplications().execute()
grid = findViewById<View>(R.id.grid) as GridView
grid!!.setChoiceMode(AbsListView.CHOICE_MODE_MULTIPLE)
grid!!.adapter = listadaptor
grid!!.onItemClickListener = AdapterView.OnItemClickListener { parent: AdapterView<*>?, view: View?, position: Int, id: Long ->
val app = applist!![position]
try {
val intent = packageManager.getLaunchIntentForPackage(app.packageName)
intent?.let { startActivity(it) }
} catch (e: Exception) {
Toast.makeText(this#appsActivity, e.message, Toast.LENGTH_LONG).show()
}
}
grid!!.onItemLongClickListener = AdapterView.OnItemLongClickListener { parent, view, position, id ->
val position1: String = (position).toString()
if (mSelected.contains(position1)) {
mSelected.remove(position1)
view.setBackgroundColor(Color.TRANSPARENT) // remove item from list
// update view (v) state here
// eg: remove highlight
} else {
mSelected.add(position1)
view.setBackgroundColor(Color.LTGRAY) // add item to list
// update view (v) state here
// eg: add highlight
}
button3.setOnClickListener(
object : View.OnClickListener {
override fun onClick(view: View?) {
val builder1: AlertDialog.Builder = AlertDialog.Builder(this#appsActivity)
builder1.setMessage("Are you sure you want to delete it ?")
builder1.setCancelable(true)
builder1.setPositiveButton(
"Yes",
DialogInterface.OnClickListener { dialog, id ->
deleteSelectedItems()
mSelected.remove(position)
listadaptor!!.notifyDataSetChanged()
val app = applist!![position]
listadaptor!!.remove(app)
})
builder1.setNegativeButton(
"No",
DialogInterface.OnClickListener { dialog, id -> dialog.cancel() })
val alert11: AlertDialog = builder1.create()
alert11.show()
}
})
true
}
}
private fun deleteSelectedItems() {
val checked: SparseBooleanArray = grid!!.getCheckedItemPositions()
if (checked != null) {
val list: List<Any> = mSelected
for (i in 0 until checked.size()) {
if (checked.valueAt(i)) {
mSelected.remove(checked.keyAt(i))
}
}
}
}
private fun checkForLaunchIntent(list: List<ApplicationInfo>): List<ApplicationInfo> {
val applist = ArrayList<ApplicationInfo>()
for (info in list) {
try {
if (null != packageManager!!.getLaunchIntentForPackage(info.packageName)) {
applist.add(info)
}
} catch (e: Exception) {
e.printStackTrace()
}
}
Collections.sort(applist, ApplicationInfo.DisplayNameComparator(packageManager))
return applist
}
#SuppressLint("StaticFieldLeak")
private inner class LoadApplications : AsyncTask<Void?, Void?, Void?>() {
override fun doInBackground(vararg params: Void?): Void? {
applist = checkForLaunchIntent(packageManager!!.getInstalledApplications(
PackageManager.GET_META_DATA))
listadaptor = ApplicationAdapter(this#appsActivity,
R.layout.grid_item, applist!!)
return null
}
override fun onPostExecute(result: Void?) {
grid!!.adapter = listadaptor
super.onPostExecute(result)
}
}
items are deleted, but after re-running the application, all installed apps will be restored in gridview
You can look into my open source project LibTron which has one module library for SharedPref written in Kotlin.
To use the library follow the instruction in Project ReadMe
Example to use the library:
val applicationInfo: ApplicationInfo = sharedprefrence.Object(name = "sharedprefKey", defaultValue = null)
Or in case you want to use it without the help of the you can use GSON, Moshi, Jackson type libraries to convert to/from string to your ApplicationInfo class while saving or reading from the Sharedprefrence
My application needs to permit additions to the listview. I've figured out how I can dynamically add to a listview by using observableArrayList. If I click on the button, an item gets added to the list and displayed.
Now I'm struggling to add a click handler (I want to handle the event that happens when someone clicks on any item within the list view). Where do I do this?
Here is my code.
package someapp
import javafx.collections.FXCollections
import javafx.geometry.Pos
import javafx.scene.layout.VBox
import javafx.scene.text.FontWeight
import tornadofx.*
class MyApp : App(HelloWorld::class) {
}
class HelloWorld : View() {
val leftSide: LeftSide by inject()
override val root = borderpane {
left = leftSide.root
}
}
class LeftSide: View() {
var requestView: RequestView by singleAssign()
override val root = VBox()
init {
with(root) {
requestView = RequestView()
this += requestView
this += button("Add Item") {
action {
requestView.responses.add( Request( "example.com",
"/foo/bar",
"{ \"foo\" : \"bar\"}".toByteArray()))
}
}
}
}
}
class RequestView : View() {
val responses = FXCollections.observableArrayList<Request>(
)
override val root = listview(responses) {
cellFormat {
graphic = cache {
form {
fieldset {
label(it.hostname) {
alignment = Pos.CENTER_RIGHT
style {
fontSize = 22.px
fontWeight = FontWeight.BOLD
}
}
field("Path") {
label(it.path)
}
}
}
}
}
}
}
class Request(val hostname: String, val path: String, val body: ByteArray) {
}
To configure a callback when an item in a ListView is selected, use the onUserSelect callback:
onUserSelect {
information("You selected $it")
}
You can optionally pass how many clicks constitutes a select as well, default is 2:
onUserSelect(1) {
information("You selected $it")
}
You are using some outdated constructs in your code, here is an updated version converted to best practices :)
class MyApp : App(HelloWorld::class)
class HelloWorld : View() {
override val root = borderpane {
left(LeftSide::class)
}
}
class LeftSide : View() {
val requestView: RequestView by inject()
override val root = vbox {
add(requestView)
button("Add Item").action {
requestView.responses.add(Request("example.com",
"/foo/bar",
"""{ "foo" : "bar"}""".toByteArray()))
}
}
}
class RequestView : View() {
val responses = FXCollections.observableArrayList<Request>()
override val root = listview(responses) {
cellFormat {
graphic = cache {
form {
fieldset {
label(it.hostname) {
alignment = Pos.CENTER_RIGHT
style {
fontSize = 22.px
fontWeight = FontWeight.BOLD
}
}
field("Path") {
label(it.path)
}
}
}
}
}
onUserSelect(1) {
information("You selected $it")
}
}
}
class Request(val hostname: String, val path: String, val body: ByteArray)