android - 如何使用按钮单击将微调器数据放入 sqlite?
问题描述
我无法将微调器的选定项目放入我的 sqlite 数据库。当我单击按钮时,我的应用程序会自动关闭。
// Get sample data from the database and display them in the spinner
spinner1=(Spinner)findViewById(R.id.spinner);
ArrayList <String> list=datahelper.getAllProvinces();
ArrayAdapter <String> adapter=new ArrayAdapter<String>(this, R.layout.spinner_layout, R.id.text, list);
spinner1.setAdapter(adapter);
ok_button=findViewById ( R.id.ok_button );
ok_button.setOnClickListener ( new View.OnClickListener () {
@Override
public void onClick(View v) {
Ok_button_onClick ( v );
}
} );
}
private void Ok_button_onClick(View v) {
spinner db = new spinner ( getApplicationContext () );
String firstspin= (String) spinner1.getItemAtPosition ( spinner1.getSelectedItemPosition () );
db.insertLabel ( firstspin );
Toast.makeText ( Main2Activity.this,"Selected Item" + firstspin, Toast.LENGTH_SHORT).show ();
我得到的错误..
D / AndroidRuntime:关闭VM E / AndroidRuntime:致命异常:主进程:com.example.newlogin,PID:14079 java.lang.NullPointerException:尝试调用虚拟方法'int android.widget.Spinner.getSelectedItemPosition()'在 com.example.newlogin.loginSQLite.Main2Activity.access$000(Main2Activity.java:24) 在 com.example.newlogin 的 com.example.newlogin.loginSQLite.Main2Activity.Ok_button_onClick(Main2Activity.java:64) 的空对象引用。 loginSQLite.Main2Activity$1.onClick(Main2Activity.java:49) at android.view.View.performClick(View.java:5610) at android.view.View$PerformClick.run(View.java:22265) at android.os。 Handler.handleCallback(Handler.java:751) 在 android.os.Handler.dispatchMessage(Handler.java:95) 在 android.os。Looper.loop(Looper.java:154) at android.app.ActivityThread.main(ActivityThread.java:6077) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.ZygoteInit$ MethodAndArgsCaller.run(ZygoteInit.java:866) 在 com.android.internal.os.ZygoteInit.main(ZygoteInit.java:756)
解决方案
额外的
从使用 ArrayList 的答案开始并不是最好的方法。
例如,您从列表中选择 ProvinceX,然后将此值插入到另一个表中,然后说 ProvinceX 将其名称更改为 ProvinceY,然后您必须将 ProvinceX 更新为 ProvinceY,只要它在其他表中使用过。没那么难,但效率低下,并且没有标准化(也就是数据重复)。
对于 SQLite,除了使用 WITHOUT ROWID 定义的特殊表之外,所有表都有一个特殊的、通常隐藏的列,称为rowid。这包含一个整数(最多 64 位有符号,因此可能有 9223372036854775807 行(如果使用负值,则为两倍))。
rowid列(通常是别名(见后文))有一些优点。可以认为是 MASTER 索引(优于 PRIMARY 索引)。搜索rowid或其别名的速度更快。
rowid 表的数据存储为 B-Tree 结构,其中每个表行包含一个条目,使用 rowid 值作为键。这意味着按 rowid 检索或排序记录很快。搜索具有特定 rowid 的记录或具有指定范围内的 rowid 的所有记录的速度大约是通过指定任何其他 PRIMARY KEY 或索引值进行的类似搜索的两倍。
用于存储rowid的存储空间将小于字符串的 a,因此在检索数据时可以缓冲更多行。
通常,表将具有专门使用 INTEGER PRIMARY KEY 的rowid的别名(例如,INT PRIMARY KEY 不会创建rowid的别名)。如果您检查答案,您可以看到_id列(即 BaseColumns._ID)使用了 INTEGER PRIMARY KEY。
因此,使用 ArrayList 可能效率低下,因为随后从数据库访问数据的唯一方法需要的效率低于对该字符串的最佳搜索。
数组列表
如果您要创建一个 Province 对象,其中包括rowid的成员变量(字段) (通常是其别名),并使用 ArrayList 作为微调器的源,那么使用该值可以提高效率(存储的数据更少,更快地访问数据,更多缓冲数据)。
如果该值是存储在相关表中的值,那么在 Province 表中将 ProvinceX 更改为 ProvinceY 将导致该值在整个过程中发生变化。所以只是 1 更新。
因此,下面的工作示例包括一个使用 ArrayList 的 Spinner 和 Button(还包括 Province 类)。
光标适配器 (SimpleCursorAdapter)
正如您所经历的那样,使用 Android SQLite SDK/API,您可以将数据放入游标中,正如本节的标题所示,ListViews 和 Spinners 的游标有适配器。在我看来,使用这些更容易,而且它们有一些优势。他们有返回_id的方法(应该是rowid列的别名)如果,这对于 CURSOR ADAPTERS 很重要,该列被专门命名为_id(因此是常量 BaseColumns._ID)。如果没有这样的列,光标适配器将崩溃。如果列不是rowid列的别名,则结果是不可预测的。
- Infact ArrayAdapters 也可以处理 id,例如有一个getSelectedItemId()。但是,这将返回的位置不是整数,而是作为一个 long onItemClickListeners 还包括作为第 4 个参数的值(同样因为没有 Cursor,该值是所选项目的位置)。
因此,您可能希望考虑以下具有 3 个 Spinners 和 Buttons(原始 ArrayList Adapter、一个 ArrayList Aapter 和一个 CursorAdapter)的示例/演示。
所有三个输出结果到日志。
示例演示代码
省类Province.java
public class Province {
private long provinceId;
private String provinceName;
public Province(long id, String name) {
this.provinceId = id;
this.provinceName = name;
}
public Province(String name) {
this(-1,name);
}
public Province() {
}
public long getProvinceId() {
return provinceId;
}
public void setProvinceId(long provinceId) {
this.provinceId = provinceId;
}
public String getProvinceName() {
return provinceName;
}
public void setProvinceName(String provinceName) {
this.provinceName = provinceName;
}
@Override
public String toString() {
return this.provinceName; // NOTE overridden to avoid using Custom adapter
}
}
- 上面没有基本的Java
活动的布局activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!" />
<Spinner
android:id="@+id/spinner"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</Spinner>
<Button
android:id="@+id/ok_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="OK">
</Button>
<Spinner
android:id="@+id/spinner2"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</Spinner>
<Button
android:id="@+id/ok2_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="OK2 (Province ArrayList)">
</Button>
<Spinner
android:id="@+id/spinner3"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</Spinner>
<Button
android:id="@+id/ok3_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="OK3 (Cursor)">
</Button>
</LinearLayout>
- 两个额外的微调器和按钮
微调器布局(未更改,因此未显示)
数据库助手
原始答案中的数据库助手根据添加了两个附加方法
//<<<<<<<<<< ADDITIONAL FOR ArrayList<Province> adapter
public ArrayList<Province> getAllProvicesAsProvinceObjects() {
ArrayList<Province> rv = new ArrayList<>();
Cursor csr = mDB.query(TABLENAME_PROVINCE,null,null,null,null,null,COL_PROVINCE_NAME);
while (csr.moveToNext()) {
rv.add(
new Province(
csr.getLong(csr.getColumnIndex(COL_PROVINCE_ID)),
csr.getString(csr.getColumnIndex(COL_PROVINCE_NAME))
)
);
}
csr.close(); //<<<<<<<<<< should always close cursors when done with them
return rv;
}
//<<<<<<<<<< ADDITIONAL FOR Cursor adapter
public Cursor getAllProvincesAsCursor() {
return mDB.query(TABLENAME_PROVINCE,null,null,null,null,null,null);
}
- 第一个 getAllProvicesAsProvinceObjects() 返回一个 ArrayList
- 第二个为游标适配器返回一个游标(因此无需构建中间 ArrayList)+1,它更容易编码。
活动 MainActivity.java
这包含代码的大部分部分中的三个。
注意管理?????? 方法,检索最新数据,构建 Spinner(一次),并管理(通过在最后的onResume方法中调用)更新列表(告诉适配器数据已更改)。
public class MainActivity extends AppCompatActivity {
DatabaseHelper datahelper;
Spinner spinner1,
spinner2, //<<<<<<<<<<ADDITONAL For ArrayList<Province> adapter
spinner3 //<<<<<<<<<<ADDITIONAL for Cursor adapter
;
Button ok_button,
ok2_button, //<<<<<<<<<<ADDITIONAL For ArrayList<Province> adapter
ok3_button //<<<<<<<<<<ADDITIONAL for Cursor adapter
;
ArrayAdapter<String> adapter; //<<<<<<<<<< declares the adapter at class level
ArrayList<String> list; //<<<<<<<<<< declares at the class level
ArrayAdapter<Province> adapter2; //<<<<<<<<<<ADDITIONAL For ArrayList<Province> adapter
ArrayList<Province> list2; //<<<<<<<<<< ADDITIONAL For ArrayList<Province> adapter
SimpleCursorAdapter adapter3; //<<<<<<<<<<ADDITIONAL for Cursor adapter
Cursor mCsr; //<<<<<<<<<<ADDITIONAL for Cursor adapter
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
datahelper = new DatabaseHelper(this);
loadDataForDemo(); //<<<<<<<<<< load some data for the demo
spinner1=(Spinner)findViewById(R.id.spinner);
spinner2 =(Spinner) findViewById(R.id.spinner2); //<<<<<<<<<< For ArrayList<Province> adapter
spinner3 = (Spinner) findViewById(R.id.spinner3); //<<<<<<<<<<ADDITIONAL for Cursor adapter
list=datahelper.getAllProvinces(); //<<<<<<<<<< CHANGED to use already decalred
adapter=new ArrayAdapter<String>(this, R.layout.spinner_layout, R.id.text, list); // <<<<<<<<< CHANGED to use already decalred
spinner1.setAdapter(adapter);
ok_button=findViewById ( R.id.ok_button );
ok_button.setOnClickListener ( new View.OnClickListener () {
@Override
public void onClick(View v) {
Ok_button_onClick (v);
}
} );
manageSpinner2(); //<<<<<<<<<< For ArrayList<Province> adapter
ok2_button = findViewById(R.id.ok2_button);
ok2_button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Ok_button2_onClick(v);
}
});
manageSpinner3(); //<<<<<<<<<<ADDITIONAL for Cursor adapter
ok3_button = findViewById(R.id.ok3_button);
ok3_button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Ok_button3_onClick(v);
}
});
}
//<<<<<<<<<< For ArrayList<Province> adapter >>>>>>>>>>
private void manageSpinner2() {
list2 = datahelper.getAllProvicesAsProvinceObjects();
if (adapter2 == null) {
adapter2 = new ArrayAdapter<>(this,R.layout.spinner_layout,R.id.text,list2);
spinner2.setAdapter(adapter2);
} else {
// Note this applies any changed data (updates, insert, deletes)
adapter2.clear();
adapter2.addAll(list2);
adapter2.notifyDataSetChanged();
}
}
//<<<<<<<<<<ADDITIONAL for Cursor adapter >>>>>>>>>>
private void manageSpinner3() {
mCsr = datahelper.getAllProvincesAsCursor();
if (adapter3 == null) {
adapter3 = new SimpleCursorAdapter(
this,
R.layout.spinner_layout,mCsr,
// Note following arrays should have matching number of elements
new String[]{DatabaseHelper.COL_PROVINCE_NAME}, // columns to get data from
new int[]{R.id.text}, // view id's (TextViews) into which data goes
// Note SimpleCursorAdapter quite flexible as layout can have multiple column/view matchings
0
);
spinner3.setAdapter(adapter3);
} else {
adapter3.swapCursor(mCsr);
}
}
private void Ok_button_onClick(View v) {
String value_via_position = list.get(spinner1.getSelectedItemPosition());
String value_from_selected_item = (String) spinner1.getSelectedItem();
Log.d("SELECTED","Selected item is " + value_via_position + " according to position.");
Log.d("SELECTED","Selected item is " + value_from_selected_item + " as per the selected item method");
long id_of_province1 = datahelper.getProvinceIdFromName(value_via_position);
long id_of_province2 = datahelper.getProvinceIdFromName(value_from_selected_item);
Log.d ("SELECTED","ID (via position) is " + String.valueOf(id_of_province1) + " ID (via selected item) is " + String.valueOf(id_of_province2));
}
private void loadDataForDemo() {
// Only load provinces if none exist
if (DatabaseUtils.queryNumEntries(datahelper.getWritableDatabase(),DatabaseHelper.TABLENAME_PROVINCE) > 0) return;
String[] provinces_to_load = new String[]{
"Hereington",
"Nothereington",
"Nowherington",
"Somewhereington",
"Overthereington",
"Inthehaystackington",
"Noweheretobefoundington",
"Atsomeplaceington",
"Zeroington",
"Beforetheotherplacington"
};
for (String province: provinces_to_load) {
datahelper.addProvince(province);
}
}
//<<<<<<<<<< For OK2 button
private void Ok_button2_onClick(View v) {
Province thisProvince = (Province) spinner2.getSelectedItem();
long id = ((Province) spinner2.getSelectedItem()).getProvinceId();
//spinner2.getSelectedItemId()
String name = ((Province) spinner2.getSelectedItem()).getProvinceName();
Log.d("SELECTED2",
"You selected Province:-" +
" ID=" + String.valueOf(thisProvince.getProvinceId()) +
" Name=" + thisProvince.getProvinceName());
}
private void Ok_button3_onClick(View v) {
long id = spinner3.getSelectedItemId(); // Cursor Adapter knows the id
String name = mCsr.getString(mCsr.getColumnIndex(DatabaseHelper.COL_PROVINCE_NAME));
Log.d("SELECTED3","You clicked ID=" + String.valueOf(id) + " Name="+name);
}
@Override
protected void onResume() {
super.onResume();
manageSpinner2(); //<<<<<<<<<< will apply changed data when returning to the activity
manageSpinner3(); //<<<<<<<<<<< likewise for Cursor adapter
}
}
推荐阅读
- javascript - 在开发greasemonkey 脚本时如何检查值和字段?
- javascript - 带有选择标签 Vue/Mongo DB 的过滤器选项
- corretto - 如何使用新版本更新 Amazon Corretto 8
- python - VS Code:python 中的 site-packges 模块没有属性?
- kotlin - kotlin:将字符串与 Int 值连接起来
- android - 我想从 Authenticator 类中的 okhttp3.Response 生成自定义错误
- spring-amqp - 使用 Spring AMQP 将消息连同错误代码一起发布到 DLX
- javascript - 防止服务器端 Blazor 在 EditForm 中触发 onsubmit
- serena - 在不知道物品规格的情况下从 Serena Dimensions 获取物品
- java - HttpMessageNotWritableException - 无法在联接表中写入 JSON