Closed. This question is
opinion-based。它当前不接受答案。
想要改善这个问题吗?更新问题,以便
editing this post用事实和引用来回答。
2年前关闭。
Improve this question
随着我对使用 Activity 和自定义 View 的熟悉,我一直在不断决定应该将与 View 及其父 Activity 相关的代码放在何处。添加需要两者都访问的自定义对象,并且如何构造代码的选项就无穷无尽。这是我的问题的具体内容:
相关类/文件:
GameActivity扩展了Activity:它使用的布局包含一些自定义 View 。
MapView扩展了View:这包含在GameActivity使用的布局中
World:定义自定义World对象的自定义类
预期结果:
GameActivity会拉出其中包含MapView的布局。 MapView的onDraw()函数使用World对象中的信息在 Canvas 上绘制 map 。
问题:
MapView所需的World对象是从以前保存的文件加载的。我可以通过多种不同方式将对象获取到MapView,这就是我犹豫不决的地方。我经历了以下迭代。 (请注意,它们都起作用。我要寻找的是使用一种方法替代另一种方法的原因。)
版本1:
GameActivity:它所做的只是setContentLayout(layout_with_mapview_in_it.xml)
MapView:具有用于从文件加载World对象的所有代码,并使用它来引用所需的绘图参数
世界:在其构造函数中带有3个参数的简单自定义类
然后,我决定从文件加载World对象应该是World类本身的方法,而不是在MapView的onCreate()方法中完成的事情。由于加载World文件是没有World对象永远做不到的事情,因此应该将它作为类方法是有意义的。因此,我在World类中创建了一个loadWorld(String world_name)方法。现在文件看起来像这样:
版本2:
GameActivity:它所做的只是setContentLayout(layout_with_mapview_in_it.xml)
MapView:使用构造函数创建一个新的World对象,然后调用它的loadWorld()方法以使用文件信息更新它
世界:在构造函数中带有3个参数的简单自定义类,以及一个loadWorld()方法
最后,我决定MapView中应该只有绘图 Activity 。拥有World对象的全部目的是要能够传递它,对吗?因此,我将“世界”构造/荷载从“ View ”移到了“Activity ”中。这导致必须在MapView中创建一个setter方法,以便能够从创建它的父 Activity 中传递World对象。
版本3:
GameActivity:设置布局,创建一个World对象,并调用它的loadWorld()方法从文件中加载它。通过ID引用MapView,然后调用MapView的setWorld()方法以将World对象的实例传递给
MapView:世界对象是从外部设置的。使用来自此对象的信息绘制 map
世界:一个简单的自定义类,在其构造函数中带有3个参数,以及一个loadWorld()方法。
好的,这就是我目前所在的位置。我的问题是,尽管我喜欢从 View 中仅包含与绘图相关的代码,并将类相关的方法保留在自己的类中的约定,但是当我切换到该方法时,似乎会突然创建更多临时对象经常将对象从一个 Activity 传递到另一个 Activity 再进行查看等。这似乎要花很多钱,但是同时这就是抽象的全部内容了吧?抽象出一个类,这样您就可以从中实例化一个对象并将其传递。但是在我看来,我越是抽象出一些东西,处理对象就变得越复杂。
我想我想问的是,是否只是不从MapView本身的文件中加载World,使事情变得更加复杂?我是否只是因为不想拥有涉及从View类中的文件读取对象的代码而固执?将其放在自己的类(class)或Activity中会更好吗?做出这些决定时需要考虑什么?有没有解决我什至不知道的难题?
我认为答案将是个人喜好,但我想知道是否有某种惯例可以采用一种或另一种惯例,或者是否有确凿的理由构造某种方式。我一直在尝试寻找该问题的答案,但我认为我使用了错误的搜索词。我一直在浏览描述如何构造 Activity/ View 结构的内容,但其中没有涉及代码的内容。当有代码时,不可避免地有人在教如何在 Activity 之间或 Activity 与 View 之间传递数据。我知道所有这些方法。我只是不知道该使用哪个。
我一直在看的一些链接:
Android App with multiple views - Best Practices?
Android - Activities vs Views
Android Activities and Views
Android: What is better - multiple activities or switching views manually?
编辑:包括有关应用程序结构和代码示例的更多详细信息
游戏 Activity :
/*IMPORT STATEMENTS REMOVED*/
public class GameActivity extends Activity implements OnTouchListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.game);
Log.d("LOGCAT", "GameActivity Started");
//get the world_name from MenuActivity
Intent intent = getIntent();
String world_name = intent.getStringExtra(MenuActivity.EXTRA_MESSAGE);
//load the world
World world = loadWorld(world_name);
//create a tilemap and get the tile translator array from it
//need to convert this to a static Map
TileMap tileMap = new TileMap(this);
Map<Integer,Bitmap> tileTranslator = tileMap.getTileTranslator();
//Create a reference to the MapView object and set the translator
MapView mapView = (MapView) findViewById(R.id.map_view);
mapView.setArgs(world, tileTranslator);
//implement the OnTouchSwipeListener
mapView.setOnTouchListener(new OnSwipeTouchListener() {
/*CODE REMOVED*/
});
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
NavUtils.navigateUpFromSameTask(this);
return true;
}
return super.onOptionsItemSelected(item);
}
public World loadWorld(String world_name) {
//Create a dummy world to load into - why?!
World dummy_world = new World();
//load the world
Log.d("LOGCAT", "Loading the World");
try {
World world = dummy_world.loadWorld(this, world_name);
return world;
} catch (IOException e) {
//do nothing!
} catch (ClassNotFoundException f) {
//do nothing!
}
return dummy_world; //if world load fails, send back the default world
// NOTE: it's not saved!!!
}
}
MapView:
/*IMPORT STATEMENTS REMOVED*/
public class MapView extends View implements OnClickListener {
protected Context context;
public World world;
public Map<Integer,Bitmap> tileTranslator;
//hardcoded variables for testing
private int tile_width = 50;
private int tile_height = 50;
public int screen_width = 12;
public int screen_height = 6;
public int playerX = 4;
public int playerY = 7;
public MapView(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
Log.d("LOGCAT", "MapView created");
setOnClickListener(this);
}
@Override
public void onDraw(Canvas canvas) {
/*CODE REMOVED*/
}
//ugly method, need to break it out into individual setters
public void setArgs(World world, Map<Integer,Bitmap> tileTranslator){
this.world = world;
this.tileTranslator = tileTranslator;
}
}
世界:
/*IMPORT STATEMENTS REMOVED*/
public class World implements Serializable {
public String world_name;
public int world_width;
public int world_height;
public int[][] world_map;
public World() { //default world - I don't even want this constructor here!
world_name = "default_world";
world_width = 1;
world_height = 1;
world_map = createWorld(world_width, world_height);
}
public World(String world_name, int world_width, int world_height) {
//set the world attributes
this.world_name = world_name;
this.world_width = world_width;
this.world_height = world_height;
//generate the map
world_map = createWorld(world_width, world_height);
}
private int[][] createWorld(int world_width, int world_height) {
//create a local tile map
int[][] world_map = new int[world_width][world_height];
//get a randomizer to fill the array with - {temporary solution}
Random rand = new Random();
//fill the tile map array with random numbers between 0 and 2
for(int row = 0; row < world_map.length; row++) {
for (int col = 0; col < world_map[row].length; col++) {
world_map[row][col] = rand.nextInt(3); //static number, needs variable!
//3 is the number of tile types
}
}
return world_map;
}
public void saveWorld(Context context, String world_name, World world) throws IOException {
FileOutputStream fos = context.openFileOutput(world_name, Context.MODE_PRIVATE);
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(world);
oos.close();
}
public World loadWorld(Context context, String world_name) throws IOException, ClassNotFoundException {
FileInputStream fis = context.openFileInput(world_name);
ObjectInputStream ois = new ObjectInputStream(fis);
World world = (World)ois.readObject();
/*this.world_name = world.world_name;
this.world_width = world.world_width;
this.world_height = world.world_height;
this.world_map = world.world_map;*/ //why doesn't this work?
return world;
}
}
删除了一些代码以节省空间。让我知道删除的代码或其他 Activity 是否对您有用。
有关幕后情况的更多详细信息:
该应用程序以MenuActivity开头,该菜单有2个按钮,每个按钮都可导致另一个 Activity :WorldGenerationActivity或WorldSelectionActivity。
在WorldGenerationActivity中,将为用户显示一个TextEdit屏幕和一个按钮。他们输入要生成的世界的参数(world_name,world_width,world_height)。他们单击按钮后,将使用给定的world_name作为文件名创建一个World对象并将其保存到文件中。通过World类中可用的saveWorld(String world_name)方法完成文件保存。新创建的World对象的实例调用了saveWorld()方法,保存了文件,并通过调用finish()将用户踢回到了父MenuActivity。
在WorldSelectionActivity中,向用户显示一个插入到ArrayAdapter中的ListView。从世界保存目录中包含的文件中创建文件名数组,并且适配器使listview可以在列表中显示这些文件名。用户选择一个,然后通过intent.putExtra()将选择作为字符串发送回parentMenuActivity。 WorldSelectionActivity是为结果而启动的,因此它结束了,我们回到MenuActivity。
MenuActivity从WorldSelectionActivity获得结果后,便将putExtra()消息存储在参数中,然后调用GameActivity。它通过另一个putExtra()将消息发送到GameActivity。
GameActivity接收消息并将其存储在一个名为world_name的变量中。然后,它创建一个World对象,并将world_name字符串传递给World类的loadWorld(String world_name)方法,该方法从先前的文件保存中加载用户要求的特定World。在我之前的解释中,我对如何处理此问题有所了解。由于我需要一个World对象才能将世界加载到其中,因此我被迫首先在GameActivity中创建一个虚拟World对象,然后调用其loadWorld方法,并将结果传递到另一个新创建的World对象中。这导致我不得不在World类中包含一个我不需要的没有参数的构造函数。我不确定为什么不先创建一个虚拟世界就无法使它工作。我尝试将文件读取逻辑放入无参数构造函数中,但这似乎也不起作用。我想我在这里遗漏了一些东西,但这并不是我目前最大的担忧。
MapView是GameView中包含的 View 之一。其onDraw()方法中的登录要求来自World对象的信息。我曾经在这里完成所有World的加载和构建,那时我只需要创建一次World并用它做我想做的任何事情。一旦将loadWorld()方法从MapView移到World类本身,并且将该方法的调用从MapView移到GameActivity,似乎我突然在整个地方创建了临时的World对象。我想知道是否有一种更清洁的方法可以解决此问题,并且仍然将事情保留在有意义的类中。
我认为您的版本3 确实比其他两个版本更好:您的模型(即World
)独立于您的 View (MapView
),而您的 Controller (GameActivity
)将它们绑定(bind)在一起。
我认为您可以改善使用Builder模式创建World
对象的方式,以便创建该对象的工作在单独的类中。让我告诉你我的意思:
public class WorldBuilder {
private File worldFile;
private String name = "default_world";
private int width = 1;
private int height = 1;
public static WorldBuilder fromFile(File worldFile){
WorldBuilder worldBuilder = new WorldBuilder();
worldBuilder.worldFile = worldFile;
return worldBuilder;
}
public WorldBuilder withName(String name){
this.name= name;
return this;
}
public WorldBuilder withWidth(int width){
this.parameter2 = param2;
return this;
}
public WorldBuilder withHeight(int height){
this.height = height;
return this;
}
public World build(){
World world = new World(name,width,height);
if(worldFile!=null)
world.loadWorld(worldFile);
return world;
}
}
在GameActivity中,您可以使用以下单行代码创建世界:
World world = WorldBuilder.fromFile(worldFile)
.withName(p1)
.withWidth(p2)
.withHeight(p3)
.build();
如果您需要使用默认参数创建一个世界,则可以简单地编写:
World world = WorldBuilder.fromFile(null).build();
编辑
在哪里写代码?
除
数据上仅依赖以外,所有计算代码都可以写入
World
类中。
切勿传递
World
作为
MapView
方法的参数(保持模型独立于 View )。
尝试以一种不在
World
中完成计算的方式来组织代码。
MapView
必须仅包含与显示直接相关的代码。
我是一名优秀的程序员,十分优秀!