我正在创建一个简单的小游戏来练习我的编码,但我偶然发现了一个问题。这不是游戏破坏,但它确实很烦人。在我的游戏中,我有一个类Tappable,这是我的ListView填充。每次点击ListView元素时,我的OnItemClickListener只会增加一个Tappable的数量。
长话短说,我使用自定义ArrayAdapter并且我在一个带有处理程序的循环上运行AsyncTask,以便计算你赚了多少钱。这个定时AsyncTask的实现已经完成,所以我可以将计算放在另一个线程上,然后更新UI onPostExecute。但是,当我点击ListView元素时,会完全忽略一些点击。更严重的是,定时AsyncTasks之间的间隔越小。
这是我的MainActivity.java
import android.os.AsyncTask;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.Locale;
public class MainActivity extends AppCompatActivity {
TextView moneyView;
ListView tappablesView;
ArrayList<Tappable> tappables = new ArrayList<>();
TappableAdapter tappableAdapter;
double money = 0; // Total amount of money you have
double time; // Var to do checks on the amount of time to finish AsyncTask
// Here is the stuff that I use as my Timer.
int millisecondsToDelay = 30;
Handler handler = new Handler();
public class WorldUpdater extends AsyncTask<Void, Integer, Double> {
@Override
protected void onPreExecute() {
super.onPreExecute();
//This is for checking how long it takes my Tasks to complete
time = System.currentTimeMillis();
}
@Override
protected Double doInBackground(Void... params) {
// Var to hold amount to add to money
double moneyAddable = 0;
// Go through the tappables arraylist
for (int i = 0; i < tappables.size(); i++) {
Tappable t = tappables.get(i);
// increase the progress to a payout for a tappable
if (t.getAmount() > 0) {
t.updateProgress();
}
// add any money that can be collected to moneyAddable
moneyAddable += t.collectMoney();
}
// To be processed in onPostExecute
return moneyAddable;
}
@Override
protected void onPostExecute(Double m) {
super.onPostExecute(m);
// add the moneyAddable from doInBackground to the amount of money you have
money += m;
// Set the moneyView text to show how much money you have
updateMoneyView();
// Tappable progress increased, so show that in the list view
tappableAdapter.notifyDataSetChanged();
// Now we see how long the task took
time = System.currentTimeMillis() - time;
// If the task took longer than the amount I want it to delay, then say how long it took
if (time > millisecondsToDelay) {
Log.i("Timer info", "Too long: " + time);
}
handler.postDelayed(new Runnable() {
@Override
public void run() {
new WorldUpdater().execute();
}
}, (((long) (millisecondsToDelay - time)) > 0)? (long) (millisecondsToDelay - time): (long) 0);
}
}
public void updateMoneyView() {
moneyView.setText(String.format(Locale.getDefault(), "$%,.2f", money));
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
moneyView = (TextView) findViewById(R.id.Money);
tappablesView = (ListView) findViewById(R.id.TappablesView);
// The two tappables in the listview
tappables.add(new Tappable("Buy me!", .25, 1, 1500));
tappables.add(new Tappable("Buy me as well!", 1, 2.25, 1000));
tappableAdapter = new TappableAdapter(this, tappables);
tappablesView.setAdapter(tappableAdapter);
tappablesView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// On item click, increase the amount of the tappable that was clicked by 1
tappables.get(position).buyAmount(1);
}
});
updateMoneyView();
// Start my timer
handler.postDelayed(new Runnable() {
@Override
public void run() {
new WorldUpdater().execute();
}
}, millisecondsToDelay);
}
@Override
protected void onDestroy() {
super.onDestroy();
handler.removeCallbacksAndMessages(null);
}
}
这是我的Tappable.java
public class Tappable {
private String title;
private double revenue, cost, revenuePerItem, costPerItem;
private int progress, progressNeeded, progressStep, amount;
private boolean canCollect = false;
private double amountToCollect = 0;
public Tappable(String title, double r, double c, int ps) {
this.title = title;
revenuePerItem = r;
amount = 0;
revenue = revenuePerItem * amount;
costPerItem = c;
cost = costPerItem * (amount + 1);
progressNeeded = 100000;
progress = 0;
progressStep = ps;
}
public void buyAmount(int n) {
amount += n;
updateCost();
updateRevenue();
}
public void updateProgress() {
progress += progressStep;
if (progress >= progressNeeded) {
progress = 0;
amountToCollect += getRevenue();
canCollect = true;
}
}
public double collectMoney() {
double amountCollectable = 0;
if (canCollect) {
amountCollectable = amountToCollect;
amountToCollect = 0;
canCollect = false;
}
return amountCollectable;
}
public void updateCost() {
cost = costPerItem * (amount + 1);
}
public void updateRevenue() {
revenue = revenuePerItem * amount;
}
public String getTitle() {
return title;
}
public int getAmount() {
return amount;
}
public double getCost() {
return cost;
}
public int getProgress() {
return progress;
}
public int getProgressNeeded() {
return progressNeeded;
}
public double getRevenue() {
return revenue;
}
}
这是我的TappableAdapter.java
import android.app.Activity;
import android.content.res.Resources;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ProgressBar;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.Locale;
public class TappableAdapter extends BaseAdapter {
private final Activity context;
private final ArrayList<Tappable> tappables;
private final Resources res;
private final LayoutInflater inflater;
public TappableAdapter(Activity context, ArrayList<Tappable> t) {
this.context = context;
tappables = t;
res = context.getResources();
inflater = context.getLayoutInflater();
}
@Override
public int getCount() {
return tappables.size();
}
@Override
public Object getItem(int position) {
return tappables.get(position);
}
@Override
public long getItemId(int position) {
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Tappable t = tappables.get(position);
View rowView = inflater.inflate(R.layout.list_item, null, true);
TextView titleView, amountView, costView, revenueView;
titleView = (TextView) rowView.findViewById(R.id.Title);
amountView = (TextView) rowView.findViewById(R.id.Amount);
costView = (TextView) rowView.findViewById(R.id.Cost);
revenueView = (TextView) rowView.findViewById(R.id.Revenue);
ProgressBar p = (ProgressBar) rowView.findViewById(R.id.progressBar);
titleView.setText(t.getTitle());
amountView.setText("Amount: " + t.getAmount());
costView.setText(String.format(Locale.getDefault(), "$%,.2f", t.getCost()));
revenueView.setText(String.format(Locale.getDefault(), "$%,.2f", t.getRevenue()));
p.setMax(t.getProgressNeeded());
p.setProgress(t.getProgress());
return rowView;
}
}
这是我的activity_main.xml
<?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.jake.mypointclicker.MainActivity">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginTop="8dp"
android:text="Simple Point Clicker"
android:textColor="@android:color/background_light"
android:textSize="36sp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
app:layout_constraintVertical_weight="0" />
<TextView
android:id="@+id/Money"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="8dp"
android:layout_marginTop="8dp"
android:text="$123,456,789.00"
android:textColor="@android:color/holo_orange_dark"
android:textSize="30sp"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView"
android:layout_marginEnd="8dp"
app:layout_constraintVertical_weight="0" />
<Button
android:id="@+id/button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginTop="8dp"
android:text="Button"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/Money"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
app:layout_constraintVertical_weight="0" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginBottom="8dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginTop="8dp"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/button"
app:layout_constraintVertical_weight="0">
<ListView
android:id="@+id/TappablesView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
</android.support.constraint.ConstraintLayout>
这是我的list_item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/black"
android:orientation="vertical"
android:padding="8dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/Title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="top|left"
android:text="Test Title"
android:textColor="@android:color/holo_blue_light"
android:textSize="20sp" />
<TextView
android:id="@+id/Revenue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="top|right"
android:text="$123,456,789.00"
android:textColor="@android:color/holo_green_light"
android:textSize="20sp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/Amount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="top|left"
android:text="Amount: 123456789" />
<TextView
android:id="@+id/Cost"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="top|right"
android:text="$123,456,789.00"
android:textColor="@android:color/holo_red_light"
android:textSize="20sp" />
</LinearLayout>
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
我尝试了很多方法来做我的计时器。我尝试使用具有相同效果的timertask的Timer。第25行左右是我的变量延迟多长时间。降低会增加忽略的点击量。提升它会产生相反的效果,但会使进度条看起来不那么平滑。就像我说的那样,它不是游戏制动问题,但是我不明白为什么当我尝试经常更新UI时,为什么不会调用onItemClick。监听器本身是否在UI线程上,当我在AsyncTask中更新UI时它是否被阻止?