我正在为Android中的Amazon S3开发文件管理器应用程序。我使用堆栈来保存文件夹对象以帮助导航。堆栈中的每个对象都是一个文件夹,其中包含该文件夹中文件和文件夹的arraylist mObjects
。
onClick事件侦听器将先前单击的对象推送到堆栈,并为所单击的对象调用arraylist的setter。
问题是推入堆栈的后续对象会更改堆栈中第一个对象的mObjects
字段。我不明白为什么,因为mObjects
字段对每个对象都有私有修饰符。
许多小时的代码盯着和谷歌搜索都变成了空白。我在Android Studio中完成了堆栈跟踪,我发现奇怪的是堆栈中第一个对象的mObjects
字段在我的BucketObjectListFragment类中的行adapter.setData(o.getmObjects())
处发生了变化。
public class BucketObjectListFragment extends ListFragment {
public static List<S3ObjectSummary> s3ObjList;
private final int position = 2;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
generateS3Objects();
for(S3ObjectSummary s : s3ObjList) {
Log.i("s3ObjList", s.getKey());
}
//Create the stack
ObjectStack myStack = new ObjectStack();
//Create a new object to hold the home list view
BucketObject o = new BucketObject();
o.setPrefixKey("/");
//Create the home object's list of folders and files
o.setHomeObjects();
//Create an adapter to hold the list of files and folders
ObjectAdapter adapter = new ObjectAdapter(o.getmObjects());
setListAdapter(adapter);
//Replicate an Android onClick event. First push the home view onto the stack.
myStack.push(o);
Log.i("Stack dump", "...");
myStack.printStack();
//Create a new BucketObject for the clicked folder.
o = ((ObjectAdapter) getListAdapter()).getItem(position);
o.setmObjects(o.getPrefixKey());
adapter.setData(o.getmObjects());
myStack.push(o);
Log.i("Stack dump", "...");
myStack.printStack();
}
public static void generateS3Objects(){
//Generate a random list of files and directories that look like S3 objects.
S3ObjList = new ArrayList<S3ObjectSummary>();
for(int i=0; i<5; i++){
S3ObjectSummary obj = new S3ObjectSummary();
s3ObjList.add(obj);
}
}
private class ObjectAdapter extends ArrayAdapter<BucketObject> {
private ObjectAdapter(ArrayList<BucketObject> objects) {
super(getActivity(), android.R.layout.simple_list_item_1, objects);
}
public void setData(ArrayList<BucketObject> items) {
clear();
setNotifyOnChange(false);
if (items != null) {
for (BucketObject item : items)
add(item);
}
notifyDataSetChanged();
}
}
}
我的BucketObject类。
public class BucketObject {
private UUID id;
private String prefixKey;
private String key;
private ArrayList<BucketObject> mObjects;
public BucketObject() {
id = UUID.randomUUID();
}
public BucketObject(String mPrefixKey, String mKey) {
id = UUID.randomUUID();
prefixKey = mPrefixKey;
key = mKey;
}
@Override
public String toString() {
return prefixKey;
}
public String getPrefixKey() {
return prefixKey;
}
public void setPrefixKey(String mPrefixKey) {
prefixKey = mPrefixKey;
}
public ArrayList<BucketObject> getmObjects(){
return mObjects;
}
public ArrayList<BucketObject> setHomeObjects(){
ArrayList<BucketObject> mFiles = new ArrayList<>();
mObjects = new ArrayList<>();
//Use a HashSet to hold unique folders and directories
Set<String> mySet = new HashSet<String>();
for (S3ObjectSummary summary : BucketObjectListFragment.s3ObjList) {
String obj = summary.getKey();
int firstSlash = obj.indexOf("/");
if (firstSlash != -1){ //if object has multiple forward slashes
String myKey = obj.substring(0, firstSlash);
if(mySet.add(myKey)){ //add to set if not a duplicate directory
BucketObject b = new BucketObject((myKey + "/"), myKey);
mObjects.add(b);
}
}
else {
//Get files
BucketObject b = new BucketObject(obj, obj);
mFiles.add(b);
}
}
mObjects.addAll(mFiles);
return mObjects;
}
public ArrayList<BucketObject> setmObjects(String prefixKey){
ArrayList<BucketObject> mFiles = new ArrayList<>();
mObjects = new ArrayList<>();
//Use a HashSet to hold unique folders and directories
Set<String> mySet = new HashSet<String>();
for (S3ObjectSummary summary : BucketObjectListFragment.s3ObjList) {
String obj = summary.getKey();
if (obj.startsWith(prefixKey)) {
String suffix = obj.replaceFirst(prefixKey, "");
int firstSlash = suffix.indexOf("/");
if (firstSlash != -1) {
String key = suffix.substring(0, firstSlash);
if (mySet.add(key)) { //add to set if not a duplicate directory
BucketObject b = new BucketObject(obj, key);
mObjects.add(b);
}
}
else{
if(!suffix.equals("")) {
BucketObject b = new BucketObject(obj, suffix);
mFiles.add(b);
}
}
}
}
mObjects.addAll(mFiles);
return mObjects;
}
}
我的ObjectStack类。
public class ObjectStack {
private Stack<BucketObject> s3ObjectStack;
public ObjectStack(){
s3ObjectStack = new Stack<>();
}
public void push(BucketObject o) {
s3ObjectStack.push(o);
}
public void printStack(){
//Prints the contents of mObjects for each BucketObject in the stack.
for(BucketObject o : s3ObjectStack){
int i = 0;
Log.i("BucketObject ", o.toString());
for(BucketObject b : o.getmObjects()){
Log.i("mObjects " + Integer.toString(i), b.toString());
i++;
}
}
}
}
生成S3对象的类。
public class S3ObjectSummary {
private final String key;
private static final String abc = "abcdefghijklmnopqrstuvwxyz";
private static SecureRandom rnd = new SecureRandom();
public S3ObjectSummary(){
key = randomString(7);
}
public String getKey(){
return key;
}
private String randomString(int len){
StringBuilder sb = new StringBuilder(len);
for(int i = 0; i < len; i++){
sb.append(abc.charAt(rnd.nextInt(abc.length())));
}
sb.insert(3, '/');
return sb.toString();
}
}
主要活动类。
public class StartActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_start);
}
}
带片段的活动的xml。
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="ie.codemaker.test_panda.StartActivity">
<fragment
android:name="ie.codemaker.test_panda.BucketObjectListFragment"
android:id="@+id/fooFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</android.support.constraint.ConstraintLayout>
编辑:我已经编辑了上述内容,以提供完整,最小化和可验证的程序。这有助于我将问题缩小到适配器的setData()
方法。即使我将单击的对象重命名为o
以外的其他内容(堆栈中的第一个对象),第一个对象的私有字段仍会在setData()
处更改。如果有人还需要Manifest.xml来运行我可以提供的代码。
答案 0 :(得分:0)
这里的问题是,正如其他人在另一个问题的答案中所指出的那样,&#34;一个典型的Java错误,指针&#34;使用Android的ArrayAdapter:https://stackoverflow.com/a/14408492/3350944
在以下行中,我将数组mObjects
传递给适配器:
ObjectAdapter adapter = new ObjectAdapter(o.getmObjects());
然后,在这一行中,我将新数组mObjects
传递给适配器,但适配器仍然指向第一个数组:
adapter.setData(p.getmObjects());
我用新的arrayList&lt;&gt;解决了这个问题在我传递给适配器的BucketObjectListFragment类中:
private ArrayList<BucketObject> newArray;
ObjectAdapter adapter = new ObjectAdapter(newArray);
然后,当我想要更新适配器时,我清除了适配器的列表,并将项目从newArray
添加到mObjects
:
viewList.clear();
for(BucketObject obj : p.getmObjects()){
newArray.add(obj);
}
adapter.notifyDataSetChanged();