I am trying to build Tic-Tac-Toe game( just for human play with android device, I have implemented a smart algorithm called minimax for my bot) by using android GridView. I have built everything successfully( UI, minimax algorithm for my bot) but there is still a hard to understand problem, that is when I click on Gridview item, I want GridView must update immediately, then my bot start it calculating.So, I have implement my code to set on GridView item click as followings:
gridviewBoard.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l){
//if it is a legal moves of player
if (isLegal(false, i)){
//put X into board
put(false, i);
//I want to update gridview immediately by using notifyDataSetChanged()
chessAdapter.notifyDataSetChanged();
//then start my bot calculating
// findBestMove is a method to find the best move for my bot
// by using minimax algorithm
int nextMove = findBestMove();
}
}
}
Howerver, the gridviewBoard did not update immediately, the gridviewBoard only updates when the findBestMove function is computed while it takes a long time to calculate. Summary, I want to update gridviewBoard immediately after item click and before my bot calculating. Can someone help me, and sorry for my bad English grammar.
Related
I'm trying to implement an autoplay feature for video items in a RecyclerView (linear vertical layout). I can't figure out how to know when a certain item is currently on/off the screen so I can autoplay/pause the video. If I put the code in onBindViewHolder method all videos start playing simultaniously. Couldn't find a solution by googling it either. Help, please!
For Recyclerview you should rely on your layout manager to give you this information.
https://developer.android.com/reference/android/support/v7/widget/GridLayoutManager
or
https://developer.android.com/reference/android/support/v7/widget/LinearLayoutManager
Assign a layout to the RecyclerView when you assign the adapter. Then use it to see what is visible.
Let me help a bit more with some Psedo Kotlin code for you to help with the player aspect.
Let's pretend you have some object that is bound into each row that can trigger the playing and has a unique ID. Let's call that an ActiveRowPlayer for this example.
NOTE*
If you are using databinding, it is simple to bind your video player playing content to a property in your model that is populating the row, but that's different story for a different post.
You can make an interface like:
interface IActivePlayerUpdater{
fun onUpdateCurrentPlayer
}
You can make a helper method like:
in your activity, you can implement an interface like :IActivePlayerUpdater and override the methods for it.
override fun onUpdateCurrentPlayer(){
var activeRowPlayer = recyclerView.layoutManager.findFirstCompletelyVisibleItemPosition()
if(activeRowplayer.someID != currentRowPlayer.someID){
currentRowPlayer.stopPlaying
currentRowPlayer = activeRowPlayer
activeRowPlayer.startPlaying
}
}
Then pass it into your adapter and just monitor your onBind method and anytime a new onBind is called that means the content has moved enough to trigger a new row item.
MyAdapterConstructor(IActivePlayerUpdater myCallback)
fun recyclerView.onBindMethod(stuffThatComesHere){
//do normal stuff
myCallback.onUpdateCurrentPlayer()
}
Keep in mind, this is just pseudo to help you on your journey. not intended to be direct copy and paste.
----NOTE* REQUESTED FROM COMMENT TO SUPPLY HOW TO TOUCH VIEWMODEL FROM OUTSIDE OF ADAPTER---
#Goran, this example I had setup a long time ago to avoid
notifyDataSetChanged on selection changed to toggle a checkbox for
each item. I eventually moved to a better option, but you asked how do
you get the viewmodel, here is a sample. rvCameraRoll was my
recyclerView, I was using it to display camera media, but that is not
relevant, just focus on getting the viewModel piece.
The only part you should care about is getting the ViewHolder, I just left the rest there in case it helps you with anything else.
int count = rvCameraRoll.getChildCount();
for (int i = 0; i < count; i++) {
MediaModelGridAdapter.ViewHolder childRow = (MediaModelGridAdapter.ViewHolder)rvCameraRoll.getChildViewHolder(rvCameraRoll.getChildAt(i));
if(isVisible) {
childRow.imgCellSelected.setVisibility(View.VISIBLE);
if(getMediaModelList().get(i).getIsSelected()){
childRow.imgCellSelected.setBackgroundResource(R.drawable.ic_ap_selected_on);
}
}else{
//check that exists, because after fresh delete list may be short while updating cells
if(getMediaModelList().size() > i) {
getMediaModelList().get(i).setIsSelected(false);
}
childRow.imgCellSelected.setBackgroundResource(R.drawable.ic_ap_selected_off);
childRow.imgCellSelected.setVisibility(View.GONE);
}
}
OLD
leaving this for anyone using a ListView
There is a OnScrollListener for ListView
You can override the
onScrollStateChanged(AbsListView view, int scrollState)
and
the
onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount)
this will give you the ListView item that is visible. So by using the onScroll you can detect which items are visible and determine which one to play and stop playing.
if you use with recyclerview
https://developer.android.com/reference/android/support/v7/widget/RecyclerView.OnScrollListener
The RecyclerView is only holding the items and the LayoutManager is responsible for displaying the items, so in order to get the ones that are visible to the user, assuming that you use LinearLayoutManager, you should call :
((LinearLayoutManager)recyclerView.getLayoutManager()).findFirstVisibleItemPosition();
or
((LinearLayoutManager)recyclerView.getLayoutManager()).findLastVisibleItemPosition();
mid project here and looking for a quick and dirty way out of a sticky situation.
The game looks like this. To play, click a card, and click another, and if they match they both stay shown. Match all and game is over.
In my case, when I have one card shown, when the user selects the second card, the processing logic, should set the card to be visible AND ONLY THEN, check if there is a match. And if not, return the card back to the old image.
This code, I hope, helps you understand what I'm trying to do.
gridLayout.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
//HOW DO I FORCE THE INTERFACE TO REDRAW BEFORE CONTINUING THE THREAD?
engine.setCardStateToShow(position);
gridLayout.invalidate();
gridLayout.invalidateViews();
try{
Thread.sleep(1000);
}catch (Exception x){
}
//DETERMINE IF THE CARDS MATCH AND IF NOT -- SET CARD INVIS
String message = engine.itemClicked(position, gridLayout);
Toast.makeText(getApplication(), message, Toast.LENGTH_SHORT).show();
//If engine returns a value of WASWINNER, we can end the activity and end the game.
if (message.equals(GameEngine.WASWINNER)) {
startActivity(endActivity);
}
}
});
}
The final result as it stands, is the application changes the image to show, checks for match, if no match changes to not show. And only afterwards, re-draws the interface again with the new values, so to the end user, if there was no match, they never see the "wrong" tile, because it is reverted back to "not show."
Thanks in advance and please correct me if the question could have been asked better.
Okay, I managed to resolve this issue making use of the AsyncTask abstract class. By using AsyncTask, I effectively could "multi thread", separating the comparison logic from the UI logic. As a result, I could manage the UI directly and seperately as shown in the basic explanation below.
By using the onPreExecute() I showed the cards.
BY using doInBackground() I could check and set card show state processing.
By using onPostExecute() I could return the cards to their final view state, directly in the UI thread, based on the results from the DoInBackground() method of the AsyncTask implementation.
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 years ago.
Improve this question
I have a screen with a list of products implemented using a recyclerview.
I am looking for a way to check how much time the user spends looking at a certain item in the list. In this way I can know in which items is he interested so I can build him an "maybe interested in" list. More exactly..if a user stays on that item more than x seconds I retain that item id and do the rest... Any kind of ideea or help helps me alot. Thanks
One option is to do something similar to Android: how to check if a View inside of ScrollView is visible? to determine which view is actually within the visible screen. It can be modified slightly to detect if the entire view is visible rather than only a portion.
You're probably also going to have to track the time started/stopped viewing in the recycle view's adapter.
I don't like idea to display user something like "maybe interested in" based on your logic. Because user has seen it already. It can be super annoying if I buy computer mouse and then I see it on every Activity I visit... I'd rather see some items from same category as most seen items but not the most seen items themself. That's why I highly suggest you to count time inside item details Activities (onStart/onStop methods)
But Answer can be something like this:
See following methods:
RecyclerView.Adapter.onBindViewHolder (View created because it is most likely going to be visible to user)
RecyclerView.Adapter.onViewRecycled (View will be recycled - 100% not visible on screen)
You can create some Map or another database column (don't know your project structure) to store item id's and timestamps from
private class MyHolder extends RecyclerView.ViewHolder {
long itemId;
long start;
public MyHolder(View itemView) {
super(itemView);
}
}
#Override
public void onBindViewHolder(BindingHolder holder, int position) {
holder.itemId = ...;
holder.start = System.currentTimeMillis();
}
#Override
public void onViewRecycled(BindingHolder holder) {
super.onViewRecycled(holder);
long currentTime = System.currentTimeMillis();
long difference = currentTime - holder.start;
// store result
map.put(holder.itemId, difference);
// to better match your needs update map item and do not overwrite it
}
This is not 100% solution, but is the simplest one.
To implement it inside Activities use same System.currentTimeMillis() method inside onStart and onStop methods (or onResume/onPause). See Acticity lifecycle so that you know where you need it.
I am writing an android app where I am using a grid view to display some items. I want to give users a configurable view where they can change the no of columns on the activity by clicking floating action button. I am changing the column no using
gridView.setNumColumns(selectedColumnNo);
This is working fine But the problem is if a user changes no of column after some scrolling the First Visible Position is set to the first item of the array list, so the user has to scroll the view again. Can someone please tell me where I am doing wrong. Or Is this the proper way to do this or should I use a different approach.
A code snippets will be helpful
Thanks.
Update::
currently I am using the bellow snippets
findViewById(R.id.fab).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int firstPosition = gv.getFirstVisiblePosition();
if(gv.getNumColumns()==2)
{
gv.setNumColumns(1);
gv.setSelection(firstPosition);
}
else {
gv.setNumColumns(2);
gv.setSelection(firstPosition);
}
}
});
Now the problem is on every 4th switch grid view is showing the first element of the arraylist
Right before you call setNumColumns(), save the GridView's first visible position:
int firstPosition = gridView.getFirstVisiblePosition();
Then, after you change the number of columns, pass that integer to setSelection():
gridView.setSelection(firstPosition);
"Selection", counter-intuitively, is not the same thing as "activation". It will ensure that the view is on-screen, but not visibly affect it in any other way.
I have two spinners that I want to be "tied" to one another in a mutually-exclusive sense: If you select an item in one, that item's text turns red and appears at the top, while the other one goes back to displaying the initial ("title") selection (if another item was previously selected), and its text turns white.
This is all done via the onItemSelected listeners:
sectionSpin.setOnItemSelectedListener(new OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> parent, View arg1,
int position, long arg3) {
issueSpin.setSelection(0);
((TextView) issueSpin.getChildAt(0)).setTextColor(Color.parseColor("#FFFFFF"));
((TextView) arg1).setTextColor(Color.parseColor("#E3170D"));
....
and vice versa for the "issue spinner". My problem is that, if I'm going from one spinner to the other and I select the top item, the onItemSelectedListener doesn't register because the item being selected is already selected.
I've been told that this is not possible. Or, rather, I've been told that it is impossible for an onItemSelected listener to fire on an item that is already selected. While I realize this is technically true, this problem seems relatively simple and I'm sure there must be a workaround to produce the desired effect.
I have a few questions regarding some that I'm pondering:
Is there any way to set all items in a spinner as unselected, while still displaying one of them?
Could I utilize a different type of event (i.e. 'setOnTouchListener', 'setOnClickListener', etc), presumably on the top item, in conjunction with the onItemSelectedListener?
Should I utilize a different type of event by itself, perhaps on the Views inflated in the spinner themselves, without the onItemSelectedListener?
Can you help me find a better strategy than those alluded to in the bulletpoints above?
Instead of using a Spinner, use a Button that visually resembles a non-active Spinner. When the button is clicked, show a custom Dialog containing a ListView. You can use the setSelection method and/or the position in the Adapter's getView to show which item is currently selected. The ListView's OnItemClickListener will get notified that something got clicked regardless of whether the item was selected or not.
When you find which item was clicked, hide the Dialog and notify the button-containing Activity of this, so that it can update both Buttons if needed.
I have implemented something really similar to this using DialogFragments and a DialogFragmentParent and it works fine.
Have you consider something like below,
sectionSpin.setOnItemSelectedListener(new OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> parent, View arg1,
int position, long arg3) {
updateView("sectionSpin");
}
}
And for other one
issueSpin.setOnItemSelectedListener(new OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> parent, View arg1,
int position, long arg3) {
updateView("issueSpin");
}
}
and now
private void updateView(String spinner){
if (spinner.equals(sectionSpin)){
//update spinner per requirement
}
else{
//update spinner per requirement
}
}
You can also consider some additional variable or parameters to keep track if the item selected for first time or not.
So, you could make the first item in the spinners not valid selections like "[Choose one]" or "[Select Title]" forcing the listener to fire every time.
This would have the added benefit that you will know user did select something before the next step.
Of course, this would depend on desired application flow.