class DialFragment: DialogFragment() {
private lateinit var image001: ImageView
private val caller = object: FingerprintManager.AuthenticationCallback() {
override fun onAuthenticationError(errorCode: Int, errString: CharSequence?) {
super.onAuthenticationError(errorCode, errString)
listener.onDialClick(errString.toString(), "2")
dismiss()
}
override fun onAuthenticationSucceeded(result: FingerprintManager.AuthenticationResult?) {
super.onAuthenticationSucceeded(result)
listener.onDialClick("yes","1")
var avp = image001.drawable as AnimatedVectorDrawable
avp.start()
dismiss()
}
override fun onAuthenticationFailed() {
super.onAuthenticationFailed()
listener.onDialClick("no","3")
dismiss()
}
The animation called by avp.start() is not beeing shown, the dialog fragment directly dismisses after succesfull authentification. if i omit the dismiss()-Call below then it works properly. My question is why is this and how to fix it? And yes, i know FingerprintManager-API is deprecated, i am running this for test purposes.
dismiss() immediately ends the life of your DialogFragment, so it will have no more opportunity to show you any animations in the Fragment. You should dismiss after a delay (however long the animation is).
override fun onAuthenticationSucceeded(result: FingerprintManager.AuthenticationResult?) {
super.onAuthenticationSucceeded(result)
listener.onDialClick("yes","1")
var avp = image001.drawable as AnimatedVectorDrawable
avp.start()
activity?.runOnUiThread(300L) { // put however long your animation takes
dismiss()
}
}
Related
When the button is clicked, data is received from the API, after which the Observer is fired, however, even with removeObservers, it is called twice.
Without removeObservers it triggers more than 2 times.
MainActivity
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
TestApiAppTheme {
Surface() {
TextInfo()
}
}
}
}
#Preview
#Composable
fun TextInfo() {
val viewModel = ViewModelProvider(this).get(MainViewModel::class.java)
var txt = remember {
mutableStateOf(0)
}
viewModel.serverInfoResponse.removeObservers(this)
viewModel.serverInfoResponse.observe(this) {
txt.value = it.players
Toast.makeText(this, "${it.players} -", Toast.LENGTH_SHORT).show()
}
Column() {
Text(
text = txt.value.toString(),
)
Button(onClick = {
viewModel.getServerInfo()
visible = true
}) {
Text("Update")
}
}
}}
ViewModel
var serverInfoResponse = MutableLiveData<ServerInfo>()
fun getServerInfo() {
viewModelScope.launch {
val apiService = ApiService.getInstance()
val info = apiService.getInfo()
serverInfoResponse.value = info
}
}
A composable function can be called multiple times, whenever the it observes changes removeObserver() and observe() should not be called directly but, rather in some effect.
The extension function observeAsState() does all work to subscribe and correctly unsubscribe for you.
Composable functions can get called many times although the compiler tries to reduce how often.
You should not have side effects in them such as adding and removing the observer. Since LiveData sends the last item out to new subscribers, every time this function composes you get the value again.
The correct way to consume live data is to convert it to a state. https://developer.android.com/reference/kotlin/androidx/compose/runtime/livedata/package-summary
But then you still have problems with showing the toast too often. For that you will need something like a LaunchEffect to make sure you only show a toast once. Might want to consider if you really need a toast or if a compose type UI would be better.
I'm working on a news app and I want my layout to become invisible when a user saves an article. Can anyone please tell me the right way to write this code?
2 things to note:
-when I run the app,the layout is visible in the "saved fragment"
but then when I add "hideSavedMessage" right next to the code that updates the recyclerView and I run the app, the layout becomes invisible.
I want the layout to be invisible only when the user saves an article.
PS: I know how the visible and invisible mode works. I have used it before. My major problem is not knowing the right place to write the code. And by layout, I mean the text view and image view that appears on the screen. I would appreciate any contributions. Thank you.
Here's my code
class SavedFragment : Fragment(R.layout.fragment_saved) {
lateinit var viewModel: NewsViewModel
lateinit var newsAdapter: SavedAdapter
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel = (activity as NewsActivity).viewModel
setupRecyclerView()
newsAdapter.setOnItemClickListener {
val bundle = Bundle().apply {
putSerializable("article", it)
}
findNavController().navigate(
R.id.action_savedFragment_to_savedArticleFragment,
bundle
)
}
val itemTouchHelperCallback = object : ItemTouchHelper.SimpleCallback(
ItemTouchHelper.UP or ItemTouchHelper.DOWN,
ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT
) {
override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
return true
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
val position =
viewHolder.adapterPosition//get position of item we deleted so that we swipe to left or right
val article =
newsAdapter.differ.currentList[position]//from news adapter at the index of the position
viewModel.deleteArticle(article)
Snackbar.make(view, "Successfully deleted article", Snackbar.LENGTH_LONG)
.apply {
setAction("Undo") {
viewModel.saveArticle(article); hideSavedMessage()
}
show()
}
val isAtLastItem = position <= 0
val shouldUpdateLayout = isAtLastItem
if (shouldUpdateLayout) {
showSavedMessage()
}
}
}
ItemTouchHelper(itemTouchHelperCallback).apply {
attachToRecyclerView(rvSavedNews)
}
viewModel.getSavedNews().observe(viewLifecycleOwner, Observer { articles ->
newsAdapter.differ.submitList(articles)
})
}
private fun setupRecyclerView() {
newsAdapter = SavedAdapter()
rvSavedNews.apply {
adapter = newsAdapter
layoutManager = LinearLayoutManager(activity)
}
}
private fun hideSavedMessage() {
savedMessage.visibility = View.INVISIBLE
isArticleAdded = false
}
private fun showSavedMessage() {
savedMessage.visibility = View.VISIBLE
}
}
The problem is that code inside observer runs even at the beginning - when you run your app, right? If I understand your problem, you just have to manage to make the fun hideSavedMessage() not be running for the first time. You could for example instantiate a boolean in onCreate() and set it to false. Then, inside the observer, you could run the hideSavedMessage() only if that boolean is true - you would set it as true at the end of the observer. I hope you understand.
I want to make my recyclerview paginationscrolling using retrofit.
I already complete get Json data using retrofit. It means interface API is correct from API Document.
However, if I loaded more 20items. can not scroll more items in Client.
when I checked Server data. per one page can get maximum 20items.
For example, if I loaded 25items in my recyclerview.
page 0: 20, page 1: 5.
if I want scrolling all items, How can I make paginationscrolling for retrofit??
check some my code and help me..
Response
Interface API
#GET("/store/cart/mine")
fun getCart(#Header("Authorization") token: String?, #Query("page") page:Int): Call<CartResponse>
CartViewActivity
class CartViewActivity : AppCompatActivity(), SwipeRefreshLayout.OnRefreshListener {
private val lastVisibleItemPosition: Int
get()= LinearLayoutManager.HORIZONTAL
private lateinit var scrollListener: RecyclerView.OnScrollListener
lateinit var mAdapter: CartItemRecyclerAdapter
#RequiresApi(Build.VERSION_CODES.N)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_cart_view)
val page = 0
val token = SharedPreference.getTokenInfo(this)
Client.retrofitService.getCart(token,page).enqueue(object :Callback<CartResponse> {
override fun onResponse(call: Call<CartResponse>, response: Response<CartResponse>) {
if (response?.isSuccessful == true) {
swipeRefreshLo.setOnRefreshListener(this#CartViewActivity)
showdata(response.body()?.docs!!)
}else if(response?.isSuccessful==false) {
val er = Gson().fromJson(response.errorBody()?.charStream(), ErrorResponse::class.java)
if (er.code==60202) {
}
}
}
override fun onFailure(call: Call<CartResponse>, t: Throwable) {
}
})
}
private fun showdata(results: MutableList<cartDocs>) {
recycler_view.apply {
mAdapter=CartItemRecyclerAdapter(context,context as Activity, results)
recycler_view.adapter=mAdapter
recycler_view.layoutManager=LinearLayoutManager(context)
setRecyclerViewScrollListener()
}
}
private fun setRecyclerViewScrollListener() {
scrollListener = object : RecyclerView.OnScrollListener() {
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
super.onScrollStateChanged(recyclerView, newState)
val totalItemCount = recyclerView.layoutManager!!.itemCount
if(totalItemCount == lastVisibleItemPosition + 1) {
Log.d("MyTAG", "Load new list")
recyclerView.removeOnScrollListener(scrollListener)
}
}
}
}
override fun onRefresh() {
swipeRefreshLo.isRefreshing = false
}
}
Firstly, you need to modify your adapter to be able to update existing data, instead of creating new adapter every time you fetch new data.
Adapter should be initialised before getting the data, to be able to call methods on it.
You should create a method inside the adapter, something like
fun updateData(results: MutableList<cartDocs>) {
dataSet.addAll(results)
notifyItemRangeInserted(start, newItemsSize)
}
Then, we you get response from server in onSuccess() you should call method above, and increment page, so the next time you load data, you get new items.
Data should be fetched first time when screen loads (page will be 0), and then when user scrolls to bottom of RV ().
I need to know the size of a Button (or any other view).
But none of the procedures in lifecycle (onCreate, onStart, OnResume) seem to know it, as the Button seems not yet to be initialized!
...
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
private var servOffset: Int=0 // Value depends on Layout/Orientation and Device
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
btPunkte.setOnClickListener { doPunkt(true) }
servOffset = btPR1.width/2 // 'btPR1' is a Button in 'Layout activity_main.*'
//ToDo: Doesn't work! = remains 0
}
override fun onResume() {
super.onResume()
// ToDo: unsolved! When do I get the size??
// onStart (or onResume) are invoked correctly, but don't know the value!
// ?? Button doesn't yet exist in Livecycle?!
servOffset = btPR1.width/2 // //ToDo: Still doesn't work!
anzeigeAktualisieren()
}
private fun anzeigeAktualisieren() {
// If ... move Button
btPR1.x += servOffset //ToDo: unsolved offset remains 0 ?!
}
private fun doPunkt(links:Boolean) {
anzeigeAktualisieren()
...
}
...
}
I did find "When are views drawn", and several other threads, but they didn't help me solve my problem.
You can try using viewTreeObserver.
val vto = button.viewTreeObserver
vto.addOnGlobalLayoutListener {
Log.e("Show me width", button.width.toString())
}
It is working, but it can and WILL be called several times!!!
Other option is to use Handler and postDelayed
Handler().postDelayed({
Log.e("Show me width2", button.width.toString())
}, 1000)
Yes, this is very bad practice, but it can save you in stupid situation :)
Good luck!
I added 3 gesture recognizers to my MapView in IB, a long press, a pan & a pinch. Their delegate is the file's owner. I set them up like so -
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
PanGestureRecognizer.AddTarget(s => { Console.WriteLine("Pan"); } );
LongPressGestureRecognizer.AddTarget(s => { Console.WriteLine("Long press"); } );
PinchGestureRecognizer.AddTarget(s => { Console.WriteLine("Pinch"); } );
}
I also implement this -
public bool ShouldRecognizeSimultaneously (UIGestureRecognizer gestureRecognizer, UIGestureRecognizer otherGestureRecognizer)
{
return true;
}
The problem is, only the Long Press gesture recognizer does anything, the others are completely ignored.
Any ideas/suggestions welcome!
Being fairly new to Monotouch, I didn't realise that when I set the delegate of the MapView in IB to my ViewController, that wouldn't actually work. I needed to create a delegate which is a subclass of UIGestureRecognizerDelegate, and set the delegate of the gestureRecognizer to this, and I added the gestureRecognizer programmatically (though that's probably not necessary) -
private class GestureRecognizerDelegate : UIGestureRecognizerDelegate
{
public override bool ShouldRecognizeSimultaneously (UIGestureRecognizer gestureRecognizer, UIGestureRecognizer otherGestureRecognizer)
{
return true;
}
}
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
UIPinchGestureRecognizer pinchGestureRecognizer = new UIPinchGestureRecognizer(s => { /* do stuff here */ } );
GestureRecognizerDelegate gestureRecognizerDelegate = new GestureRecognizerDelegate();
pinchGestureRecognizer.Delegate = gestureRecognizerDelegate;
MapView.AddGestureRecognizer(pinchGestureRecognizer);
}
Then, by setting the ZoomEnabled property of the MapView to false, I can control how the map zooms (in my case, I had to prevent the map zooming in beyond a certain threshold, my client wasn't happy with the way you could zoom in & it would then bounce back out to my preset value, which I had working using RegionChanged in the MapView delegate). Don't you love clients!