Android备考01 黑马76期-day03 操作数据库
生活随笔
收集整理的這篇文章主要介紹了
Android备考01 黑马76期-day03 操作数据库
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
?
?
數據庫創建以及原始方式操作數據庫
activity_main.xml
<LinearLayout 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"android:orientation="vertical"tools:context=".MainActivity" ><Buttonandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:onClick="click1"android:text="add" /><Buttonandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:onClick="click2"android:text="delete" /><Buttonandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:onClick="click3"android:text="update" /><Buttonandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:onClick="click4"android:text="find" /></LinearLayout>MyOpenHelper.java
package com.example.sqlite2;import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper;public class MyOpenHelper extends SQLiteOpenHelper {/*** @param context 上下文* @param name 數據庫的名字* @param factory 創建cursor對象(就是游標也即結果集)* @param version 數據庫的版本* */public MyOpenHelper(Context context) {super(context, "itheima.db", null, 3);}/*** 數據庫第一創建時調用* 特別適合做表格結構的初始化 * 那么可得此方法就是寫創建表的sql語句的*/@Overridepublic void onCreate(SQLiteDatabase db) {//1. sqlite的id一般寫成 "_id"//2. 數據類型統一為String 寫了和沒寫一樣 為何和數據庫程序員融洽對接以及增加可讀性 企業還是要求規范寫db.execSQL("create table info(_id integer primary key autoincrement,name varchar(20))");}/*** 數據庫版本升級時調用* 適合于更新表結構*/@Overridepublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {db.execSQL("alter table info add phone varchar(20)");}}MainActivity.java
package com.example.sqlite2;import android.os.Bundle; import android.app.Activity; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.view.Menu; import android.view.View; import android.widget.Toast;public class MainActivity extends Activity {private MyOpenHelper helper;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);helper = new MyOpenHelper(getApplicationContext());//打開或者創建數據庫 第一次創建//SQLiteDatabase writableDatabase = helper.getWritableDatabase();//打開或者創建數據庫 第一次創建 若磁盤滿 返回一個只讀的 // SQLiteDatabase readableDatabase = helper.getReadableDatabase();}//點擊按鈕增加一條記錄public void click1(View v){//[1]獲取數據庫對象SQLiteDatabase db = helper.getWritableDatabase();//[2]執行增加一條記錄的sql語句db.execSQL("insert into info(name,phone) values(?,?);",new Object[]{"張三","14575651"});//[3]數據庫用完關閉db.close();}//刪除public void click2(View v){SQLiteDatabase db = helper.getWritableDatabase();db.execSQL("delete from info where name=?;", new Object[]{"張三"});db.close();}//更新public void click3(View v){SQLiteDatabase db = helper.getWritableDatabase();db.execSQL("update info set phone=? where name=?;", new Object[]{"110120","張三"});db.close();}//查詢public void click4(View v){SQLiteDatabase db = helper.getReadableDatabase();//獲取游標 即結果集Cursor cursor = db.rawQuery("select * from info;", null);if(cursor.getCount()<=0){Toast.makeText(this, "數據庫空", 0).show();return;}StringBuffer ans=new StringBuffer();//一行一行獲取數據while(cursor.moveToNext()){//編號重0開始 0是id不取String name = cursor.getString(1);String phone = cursor.getString(2);ans.append("\nname:"+name+" phone:"+phone);}Toast.makeText(this,ans, 0).show();}}?
小結1
容易進行多表查詢
?
命令行訪問sqlite數據庫
adb shell
cd data/data
ls
cd com.example.sqlite2
ls
cd databases
ls
sqlite3 itheima.db
select * from info;
提示:sqlite3: not found
需要下載安裝sqlite3工具
★改變dos窗口的編碼(解決某些亂碼)
改為gbk:? chcp 936
改為u8: chcp 65001
?
?
使用api進行crud
有諸多好處,但寫起來特別復雜,不如直接寫sql語句
MainActivity.java
package com.example.sqlite2;import android.app.Activity; import android.content.ContentValues; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.os.Bundle; import android.view.View; import android.widget.Toast;import com.example.crud.R;public class MainActivity extends Activity {private MyOpenHelper helper;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);helper = new MyOpenHelper(getApplicationContext());//打開或者創建數據庫 第一次創建//SQLiteDatabase writableDatabase = helper.getWritableDatabase();//打開或者創建數據庫 第一次創建 若磁盤滿 返回一個只讀的 // SQLiteDatabase readableDatabase = helper.getReadableDatabase();}//點擊按鈕增加一條記錄public void click1(View v){//[1]獲取數據庫對象SQLiteDatabase db = helper.getWritableDatabase();//[2]執行增加一條記錄的sql語句//db.execSQL("insert into info(name,phone) values(?,?);",new Object[]{"張三","14575651"});/*** db.insert(table, nullColumnHack, values)* * table:表名* values:ContentValues類內封裝一個map key:列名 value:插入的值*/ContentValues values=new ContentValues();values.put("name", "王五");values.put("phone", "110");//返回值為插入新行的idlong insert = db.insert("info", null, values);//insert底層就在封裝sql語句//[3]數據庫用完關閉db.close();if(insert>0){Toast.makeText(getApplicationContext(), "添加成功!", 0).show();}else{Toast.makeText(getApplicationContext(), "添加失敗!", 0).show();}}//刪除public void click2(View v){SQLiteDatabase db = helper.getWritableDatabase();//db.execSQL("delete from info where name=?;", new Object[]{"張三"});/*** db.delete(table, whereClause, whereArgs)* * table:所刪表名* whereClause:刪除條件* whereArgs: 參數 注意是new String[]{}* * 返回值:影響的行數*/int delete = db.delete("info", "name=?", new String[]{"王五"});db.close();Toast.makeText(getApplicationContext(), "刪除了"+delete+"行!", 0).show();}//更新public void click3(View v){SQLiteDatabase db = helper.getWritableDatabase();//db.execSQL("update info set phone=? where name=?;", new Object[]{"110120","張三"});/*** db.update(table, values, whereClause, whereArgs)* * table:表名* values:ContentValues key:修改的列名 values:新的列值* whereClause:條件* whereArgs:參數* 返回值:更新了多少行*/ContentValues values=new ContentValues();values.put("phone", "114"); int update = db.update("info", values, "name=?", new String[]{"王五"});db.close();Toast.makeText(getApplicationContext(), "更新了"+update+"行!", 0).show();}//查詢public void click4(View v){SQLiteDatabase db = helper.getReadableDatabase();//獲取游標 即結果集//Cursor cursor = db.rawQuery("select * from info;", null);/*** db.query(table, columns, selection, selectionArgs, groupBy, having, orderBy)* 見名識義* columns:String[] 要查詢的列名 null代表查詢所有* selection: 查詢條件* selectionArgs: 查詢參數* groupBy:分組條件* having: 過濾條件* orderBy:排序條件* * eg:db.query("info",new String[]{"phone"},"name=?",new String[]{"王五"},null,null,null);*/Cursor cursor = db.query("info",new String[]{"phone"},"name=?",new String[]{"王五"},null,null,null);if(cursor.getCount()<=0){Toast.makeText(this, "數據庫空", 0).show();return;}StringBuffer ans=new StringBuffer();//一行一行獲取數據while(cursor.moveToNext()){//編號重0開始 注意此時只查詢一列 phone就是0String phone = cursor.getString(0);ans.append("\nphone:"+phone);}Toast.makeText(this,ans, 0).show();}}?
?
?
Android事務模板(轉賬)
activity_main.xml
<RelativeLayout 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"android:paddingBottom="@dimen/activity_vertical_margin"android:paddingLeft="@dimen/activity_horizontal_margin"android:paddingRight="@dimen/activity_horizontal_margin"android:paddingTop="@dimen/activity_vertical_margin"tools:context=".MainActivity" ><TextViewandroid:id="@+id/textView1"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="@string/hello_world" /><Buttonandroid:id="@+id/button1"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignLeft="@+id/textView1"android:layout_below="@+id/textView1"android:layout_marginLeft="29dp"android:layout_marginTop="80dp"android:onClick="click"android:text="轉賬" /><Buttonandroid:id="@+id/button2"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignLeft="@+id/button1"android:layout_below="@+id/button1"android:layout_marginTop="125dp"android:onClick="click2"android:text="查詢" /></RelativeLayout>MyOpenHelper.java
package com.example.transaction;import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase.CursorFactory; import android.database.sqlite.SQLiteOpenHelper;public class MyOpenHelper extends SQLiteOpenHelper {public MyOpenHelper(Context context) {super(context, "Account", null, 1);}@Overridepublic void onCreate(SQLiteDatabase db) {db.execSQL("create table info(_id integer primary key autoincrement,name varchar(20),phone varchar(20),account varchar(20))");db.execSQL("insert into info(name,phone,account) values('張三','110','3000')");db.execSQL("insert into info(name,phone,account) values('李四','120','3000')");}@Overridepublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {}}MainActivity.java
package com.example.transaction;import android.os.Bundle; import android.app.Activity; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.view.Menu; import android.view.View; import android.widget.Toast;public class MainActivity extends Activity {private SQLiteDatabase db;private SQLiteDatabase db2;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);MyOpenHelper myOpenHelper = new MyOpenHelper(getApplicationContext());db = myOpenHelper.getWritableDatabase();db2 = myOpenHelper.getReadableDatabase();}public void click(View v) {db.beginTransaction();//開啟事務try {db.execSQL("update info set account=account-100 where name=?",new String[] { "張三" });int i=1/0;db.execSQL("update info set account=account+100 where name=?",new String[] { "李四" });// 未發生異常會執行到此行,也就會幫你提交// 發生異常就不會執行到此行,自動回滾db.setTransactionSuccessful();Toast.makeText(getApplicationContext(), "轉賬成功!", 0).show();} catch (Exception e) {Toast.makeText(getApplicationContext(), "服務器忙,請稍后再試試~~", 0).show();} finally {db.endTransaction();//關閉事務}}//查詢所有public void click2(View v) {Cursor cursor = db2.rawQuery("select name,phone,account from info",null);StringBuffer sb = new StringBuffer();while (cursor.moveToNext()) {sb.append(cursor.getString(0) + "\t" + cursor.getString(1) + "\t"+ cursor.getString(2) + "\n");}Toast.makeText(getApplicationContext(), sb, 0).show();} }?
ListView控件入門
activity_main.xml里寫一個文件,MainActivity.java里用java代碼布局顯示內容
activity_main.xml
<RelativeLayout 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"android:paddingBottom="@dimen/activity_vertical_margin"android:paddingLeft="@dimen/activity_horizontal_margin"android:paddingRight="@dimen/activity_horizontal_margin"android:paddingTop="@dimen/activity_vertical_margin"tools:context=".MainActivity" ><ListViewandroid:id="@+id/lv"android:layout_width="match_parent"android:layout_height="match_parent" ></ListView></RelativeLayout>MainActivity.java
package com.example.listview;import android.os.Bundle; import android.app.Activity; import android.view.Menu; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.ListView; import android.widget.TextView;public class MainActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//[1]找到關心的控間ListView lv = (ListView) findViewById(R.id.lv);//[2]顯示數據 textview和其他普通控件有點區別 它的數據來源于數據適配器lv.setAdapter(new MyListAdapter());}/*** 初始化布局代碼* @author Administrator**///[3] 定義listview的數據適配器private class MyListAdapter extends BaseAdapter{//一共顯示多少條目@Overridepublic int getCount() {return 100;}//返回指定position(重0開始)處的對象 用默認即可@Overridepublic Object getItem(int position) {return null;}//返回position位置對應的id@Overridepublic long getItemId(int position) {return 0;}/*** 獲取一個view 用來顯示listview的數據 會作為listview的一個條目出現* 每條顯示多少*/@Overridepublic View getView(int position, View convertView, ViewGroup parent) {TextView tv=new TextView(MainActivity.this);tv.setText("哈哈哈"+position);return tv;}} }?
ListView優化
布局加滾動條
items采用復用技術,使得大大節省內存,也絕對不會內存溢出
activity_main.xml
<RelativeLayout 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"android:paddingBottom="@dimen/activity_vertical_margin"android:paddingLeft="@dimen/activity_horizontal_margin"android:paddingRight="@dimen/activity_horizontal_margin"android:paddingTop="@dimen/activity_vertical_margin"tools:context=".MainActivity" ><ListViewandroid:id="@+id/lv"android:fastScrollEnabled="true"android:layout_width="match_parent"android:layout_height="match_parent" ></ListView></RelativeLayout>MainActivity.java
package com.example.listview;import android.os.Bundle; import android.app.Activity; import android.view.Menu; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.ListView; import android.widget.TextView;public class MainActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//[1]找到關心的控間ListView lv = (ListView) findViewById(R.id.lv);//[2]顯示數據 textview和其他普通控件有點區別 它的數據來源于數據適配器lv.setAdapter(new MyListAdapter());}/*** 初始化布局代碼* @author Administrator**///[3] 定義listview的數據適配器private class MyListAdapter extends BaseAdapter{//一共顯示多少條目@Overridepublic int getCount() {return 1000;}//返回指定position(重0開始)處的對象 用默認即可@Overridepublic Object getItem(int position) {return null;}//返回position位置對應的id@Overridepublic long getItemId(int position) {return 0;}/*** 獲取一個view 用來顯示listview的數據 會作為listview的一個條目出現* 每條顯示多少*/@Overridepublic View getView(int position, View convertView, ViewGroup parent) { // System.out.println("listview"+position);//說明界面上顯示一條才調用一次本方法 // TextView tv=new TextView(MainActivity.this);//每次都new 內存不夠快速拖拽,前面的沒來得及釋放就會內存溢出TextView tv;//簡簡單單的判斷保證快速滑動也不會卡死 復用使得只用創建一屏能顯示的條目數個tv就行了if(convertView==null){tv=new TextView(MainActivity.this);System.out.println("創建view對象---"+position);}else{System.out.println("復用歷史緩存對象---"+position);tv=(TextView) convertView;}tv.setText("哈哈哈"+position);return tv;}} }?
?
奇怪現象
listview布局中的android:layout_height="match_parent"? ?都寫match_parent? 不要用wrap_content
都這么寫:
<ListViewandroid:id="@+id/lv"android:layout_width="match_parent"android:layout_height="match_parent"/>包裹內容就無法確定高度,也就無法準確計算出一屏一共需要new出多少個items就會多次嘗試最終new出很多多余的item,效率會低點
?
?
listview練習復雜布局
activity_main.xml
<RelativeLayout 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=".MainActivity" ><ListViewandroid:id="@+id/lv"android:fastScrollEnabled="true"android:layout_width="match_parent"android:layout_height="match_parent" /></RelativeLayout>item.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_marginTop="10dp"android:layout_width="match_parent"android:layout_height="wrap_content" ><ImageViewandroid:id="@+id/iv_icon"android:layout_width="wrap_content"android:layout_height="wrap_content"android:src="@drawable/pci" /><TextView android:id="@+id/iv_title"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_toRightOf="@+id/iv_icon"android:singleLine="true"android:ellipsize="end"android:textColor="#000000"android:textSize="20sp"android:text="畢業生拒絕年薪300萬工作 選擇年薪200萬的華為"/><TextView android:id="@+id/iv_messgae"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginTop="5dp"android:layout_toRightOf="@+id/iv_icon"android:layout_below="@+id/iv_title"android:textColor="#999999"android:textSize="15sp"android:text="今天,剛畢業就年薪200萬”的話題沖上熱搜!他是誰?"/></RelativeLayout>MainActivity.java
package com.example.listviewLX;import android.os.Bundle; import android.app.Activity; import android.view.Menu; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.LinearLayout; import android.widget.ListView;public class MainActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);// [1]找到關心的控件ListView lv=(ListView) findViewById(R.id.lv);//[2]設置適配器布局lv.setAdapter(new MyAdapter());}public class MyAdapter extends BaseAdapter{@Overridepublic int getCount() {return 1000;}@Overridepublic Object getItem(int position) {return null;}@Overridepublic long getItemId(int position) {return 0;}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {//[1]想辦法將自定義的布局轉換為一個viewView view;if(convertView==null){/*** View.inflate(context, resource, root)* * 創建新的view對象 通過打氣筒inflate將一個xml布局轉換為一個view* resource就是xml文件 通過R.layout找到* root:ViewGroup類型 LinearLayout,reaLayout都繼承自ViewGroup,這種類型可以有自己的孩子* root即用來指定默認的父親 此處只為了轉換為view,故用不到*/view = View.inflate(getApplicationContext(), R.layout.item, null);}else{//復用歷史緩存對象view=convertView;}return view;}} }?
獲取打氣筒各種方法
3種方法,第一種最簡單,第三種最常用
// 獲取打氣筒方法一 view = View.inflate(getApplicationContext(), R.layout.item,null);// 獲取打氣筒方法二 view = LayoutInflater.from(getApplicationContext()).inflate(R.layout.item, null);//獲取打氣筒方法三 LayoutInflater layoutInflater=(LayoutInflater)getSystemService(LAYOUT_INFLATER_SERVICE); view = layoutInflater.inflate(R.layout.item, null);?
MainActivity.java
package com.example.listviewLX;import android.os.Bundle; import android.app.Activity; import android.view.LayoutInflater; import android.view.Menu; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.LinearLayout; import android.widget.ListView;public class MainActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);// [1]找到關心的控件ListView lv = (ListView) findViewById(R.id.lv);// [2]設置適配器布局lv.setAdapter(new MyAdapter());}public class MyAdapter extends BaseAdapter {@Overridepublic int getCount() {return 1000;}@Overridepublic Object getItem(int position) {return null;}@Overridepublic long getItemId(int position) {return 0;}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {// [1]想辦法將自定義的布局轉換為一個viewView view;if (convertView == null) {/*** View.inflate(context, resource, root)* * 創建新的view對象 通過打氣筒inflate將一個xml布局轉換為一個view resource就是xml文件* 通過R.layout找到 root:ViewGroup類型* LinearLayout,reaLayout都繼承自ViewGroup,這種類型可以有自己的孩子* root即用來指定默認的父親 此處只為了轉換為view,故用不到*/// 獲取打氣筒方法一// view = View.inflate(getApplicationContext(), R.layout.item,null);// 獲取打氣筒方法二//view = LayoutInflater.from(getApplicationContext()).inflate(R.layout.item, null);//獲取打氣筒方法三LayoutInflater layoutInflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);view = layoutInflater.inflate(R.layout.item, null);} else {// 復用歷史緩存對象view = convertView;}return view;}} }?
每項顯示單一數據(textview)的listview適配器 arrayAdapter
activity_main.xml
<RelativeLayout 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"android:paddingBottom="@dimen/activity_vertical_margin"android:paddingLeft="@dimen/activity_horizontal_margin"android:paddingRight="@dimen/activity_horizontal_margin"android:paddingTop="@dimen/activity_vertical_margin"tools:context=".MainActivity" ><ListViewandroid:id="@+id/lv"android:layout_width="match_parent"android:layout_height="match_parent" ></ListView></RelativeLayout>item.xml
<?xml version="1.0" encoding="utf-8"?> <TextView xmlns:android="http://schemas.android.com/apk/res/android"android:id="@+id/textView1"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="TextView" />item2.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="match_parent"android:orientation="vertical" ><TextViewandroid:id="@+id/tv2"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="TextView" /></LinearLayout>MainActivity.java
package com.example.arrayadapter;import android.os.Bundle; import android.app.Activity; import android.view.Menu; import android.view.View; import android.widget.ArrayAdapter; import android.widget.ListView;public class MainActivity extends Activity {String objects[]={"天河","紫英","夢璃","菱紗","天青","玄霄","夙玉"};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);// [1]找到關心的控件ListView lv = (ListView) findViewById(R.id.lv);//顯式單一文本信息時不必要用basedaptor 用arrayadptor即可//[2]創建一個arrayAdapter (注意此處的adapter的作用都只是專門顯式item到listview上)//方法1:注意自定義的R.layout.item只能是個textview 外面不能包什么布局//ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, R.layout.item, objects);//方法2:自定義的R.layout.item2里可以有布局包裹了,但是一定要給定義的textview定義id 需要特別指定ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, R.layout.item2, R.id.tv2, objects);//[3]設置數據適配器lv.setAdapter(adapter);} }?
?
?
?
?
?
?
?
?
?
?
?
?
總結
以上是生活随笔為你收集整理的Android备考01 黑马76期-day03 操作数据库的全部內容,希望文章能夠幫你解決所遇到的問題。