مستندات جامع اندروید
نسخه 1.0.3
هدف این لایه، دریافت نقاطی از پایگاه داده و نمایش یک نشانگر به ازای هر کدام از آنها بر روی نقشه است.
فهرست مطالب این صفحه
فایل database.sqlite
در پوشه assets
قرار دادهایم.
activity_database_layer.xml
:
در این صفحه، علاوه بر المان نقشه نشان یک ToggleButton
نیز قرار گرفته است که وظیفه آن فعال و غیرفعال کردن لایه پایگاه داده است.
<?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"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".activity.DatabaseLayer">
<org.neshan.mapsdk.MapView
android:layout_width="0dp"
android:layout_height="0dp"
android:id="@+id/map"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
<ToggleButton
android:checked="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/toggle_button_text_color"
android:textOff="لایه دیتابیس"
android:textOn="لایه دیتابیس"
android:elevation="8dp"
android:paddingStart="8dp"
android:paddingEnd="8dp"
android:drawableStart="@drawable/ic_database_layer"
android:drawableTint="@color/toggle_button_text_color"
android:drawablePadding="8dp"
android:background="@drawable/toggle_button_bg"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
tools:targetApi="m"
android:onClick="toggleDatabaseLayer"/>
</android.support.constraint.ConstraintLayout>
AssetDatabaseHelper.java
:
وظیفه این کلاس، کپی کردن فایل پایگاه داده به حافظه داخلی موبایل، باز کردن پایگاه داده و برگرداندن آن است.
آدرس مسیر در حافظه داخلی تلفن همراه را در متغیر DB_PATH
و نام فایل پایگاه داده را در متغیر DB_NAME
ذخیره کردهایم.
// CHANGE THIS TWO LINE OF CODES!
//The Android's default system path of your application database.
private static String DB_PATH = "/data/data/org.neshan.sample.starter/databases/";
private static String DB_NAME = "database.sqlite";
بقیه بخشهای این کلاس به طور کامل کامنتگذاری شدهاست. برای کپی کردن پایگاه داده به حافظه داخلی تلفنهمراه میتوانید از این helper استفاده کنید یا helper خود را بنویسید.
public class AssetDatabaseHelper extends SQLiteOpenHelper {
// CHANGE THIS TWO LINE OF CODES!
//The Android's default system path of your application database.
private static String DB_PATH = "/data/data/org.neshan.sample.starter/databases/";
private static String DB_NAME = "database.sqlite";
private SQLiteDatabase myDataBase;
private final Context myContext;
/**
* Constructor
* Takes and keeps a reference of the passed context in order to access to the application assets and resources.
* @param context
*/
public AssetDatabaseHelper(Context context) {
super(context, DB_NAME, null, 1);
this.myContext = context;
}
/**
* Creates a empty database on the system and rewrites it with your own database.
* */
public void createDataBase() throws IOException {
//By calling this method and empty database will be created into the default system path
//of your application so we are gonna be able to overwrite that database with our database.
this.getReadableDatabase();
try {
copyDataBase();
} catch (IOException e) {
throw new Error("Error copying database");
}
}
/**
* Check if the database already exist to avoid re-copying the file each time you open the application.
* @return true if it exists, false if it doesn't
*/
private boolean checkDataBase(){
SQLiteDatabase checkDB = null;
try{
String myPath = DB_PATH + DB_NAME;
checkDB = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READONLY);
}catch(SQLiteException e){
//database does't exist yet.
}
if(checkDB != null){
checkDB.close();
}
return checkDB != null ? true : false;
}
/**
* Copies your database from your local assets-folder to the just created empty database in the
* system folder, from where it can be accessed and handled.
* This is done by transfering bytestream.
* */
private void copyDataBase() throws IOException{
//Open your local db as the input stream
InputStream myInput = myContext.getAssets().open(DB_NAME);
// Path to the just created empty db
String outFileName = DB_PATH + DB_NAME;
//Open the empty db as the output stream
FileOutputStream myOutput = new FileOutputStream(outFileName, false);
//transfer bytes from the inputfile to the outputfile
byte[] buffer = new byte[1024];
int length;
while ((length = myInput.read(buffer))>0){
myOutput.write(buffer, 0, length);
}
//Close the streams
myOutput.flush();
myOutput.close();
myInput.close();
}
public SQLiteDatabase openDataBase() throws SQLException {
//Open the database
String myPath = DB_PATH + DB_NAME;
myDataBase = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READONLY);
return myDataBase;
}
@Override
public synchronized void close() {
if(myDataBase != null)
myDataBase.close();
super.close();
}
@Override
public void onCreate(SQLiteDatabase db) {
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
// Add your public helper methods to access and get content from the database.
// You could return cursors by doing "return myDataBase.query(....)" so it'd be easy
// to you to create adapters for your views.
}
DatabaseLayer
یک شی از نوع SQLiteDatabase
برای ذخیره و کار با پایگاه داده نقاط در نظر گرفته میشود.
// our database points
SQLiteDatabase pointsDB;
متد initLayoutRefrences
جهت مقداردهی اولیه کردن به تمامی المانهای مربوط به رابط کاربری نوشته شدهاست. به دلیل این که لازم است تا المان اندرویدی نقشه نشان ابتدا به طور کامل ایجاد شود و سپس با آن تعامل برقرار شود (متدهای مختلف بر روی المان نقشه اجرا شود)، تمامی متدهای مربوط به رابط کاربری باید در متد onStart
انجام شوند.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// starting app in full screen
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.activity_database_layer);
}
@Override
protected void onStart() {
super.onStart();
// everything related to ui is initialized here
initLayoutReferences();
}
پس از مقداردهی اولیه viewها و نقشه، متد getDBPoints
صدا زده میشود که جلوتر توضیح داده خواهد شد.
// Initializing layout references (views, map and map events)
private void initLayoutReferences() {
// Initializing views
initViews();
// Initializing mapView element
initMap();
// copy database.sqlite file from asset folder to /data/data/... and read points and add marker on map
getDBPoints();
}
در این متد، یک شی از کلاس AssetDatabaseHelper
با نام myDbHelper
ساخته میشود. در ادامه متد createDataBase
بر روی شی myDbHelper
صدا زده میشود. در صورت موفق بودن، یک پایگاه داده در حافظه داخلی تلفن همراه ایجاد شده و محتویات فایل database.sqlite
– که در پوشه assets
وجود دارد – به دیتابیس ساخته شده منتقل میشود.
سپس، پایگاه داده ایجاد شده با استفاده از متد openDataBase
باز میشود و شی بازگردانده شده توسط این متد که از نوع SQLiteDatabase
است، در متغیر pointsDB
ذخیره میشود.
در ادامه یک Cursor
ایجاد شده و با استفاده از متد rawQuery
یک کوئری برای دریافت تمام ردیفهای پایگاه داده – در هر ردیف مختصات جغرافیایی یک نقطه وجود دارد – اجرا می شود. با صدا زدن متد addMarker
به ازای هر ردیف از اطلاعات، نشانگری در موقعیت جغرافیایی به دست آمده بر روی نقشه نشان داده خواهد شد. در ادامه متد moveToNext
بر روی cursor
صدا زده می شود تا همین عملیات در مرحله بعد بر روی داده بعدی انجام شود. همنچین هر نشانه ای که به نقشه اضافه میشود به آرایه markers
هم افزوده میشود تا در ادامه جهت حذف یا تغییرات روی نشانه ها در دسترس باشد.
با استفاده از شی cursor
اطلاعات مربوط به lat و lng هر ردیف به دست میآید و یک شی از نوع LatLng
از این اطلاعات ساخته میشود. همزمان، اندازه lat و lng مینیمم و ماکزیمم را برای مشخص کردن محدودهای که قرار است از نقشه نمایش داده شود به دست میآید.
برای محدود کردن ناحیهای از نقشه که قرار است نمایش داده شود، متد moveToCameraBounds
بر روی map
صدا زده میشود. ورودی اول این متد، یک شی از نوع LatLngBounds
است که مختصات نقطه شمال شرق و جنوب غرب به عنوان ورودی به آن داده میشود. دومین آرگومان ورودی این متد، یک ScreenBounds
است که دو ورودی از نوع ScreenPos
به آن داده میشود که اولین ورودی نقطه ۰ و ۰ است و دومین ورودی اندازه عرض و طول نقشه است. به این ترتیب زوم و مکان نقشه به شکلی تنظیم میشود که محدوده مختصات مشخص شده ( پارامتر اول ) در کل بخش نمایشی نقشه ( پارامتر دوم ) به نمایش در بیاید.
// copy database.sqlite file from asset folder to /data/data/... and read points and add marker on map
private void getDBPoints(){
// we create an AssetDatabaseHelper object, create a new database in mobile storage
// and copy database.sqlite file into the new created database
// Then we open the database and return the SQLiteDatabase object
AssetDatabaseHelper myDbHelper = new AssetDatabaseHelper(this);
try {
myDbHelper.createDataBase();
}
catch (IOException ioe) {
throw new Error("Unable to create database");
}
try {
pointsDB = myDbHelper.openDataBase();
}
catch(SQLException sqle){
throw sqle;
}
// creating a cursor and query all rows of points table
Cursor cursor = pointsDB.rawQuery("select * from points",null);
//reading all points and adding a marker for each one
if (cursor.moveToFirst()) {
map.getLayers().add(markerLayer);
// variable for creating bound
// min = south-west
// max = north-east
double minLat = Double.MAX_VALUE;
double minLng = Double.MAX_VALUE;
double maxLat = Double.MIN_VALUE;
double maxLng = Double.MIN_VALUE;
while (!cursor.isAfterLast()) {
double lng = cursor.getDouble(cursor.getColumnIndex("lng"));
double lat = cursor.getDouble(cursor.getColumnIndex("lat"));
LatLng LatLng = new LatLng(lat,lng);
// validating min and max
minLat = Math.min(LatLng.getLatitude(), minLat);
minLng = Math.min(LatLng.getLongitude(), minLng);
maxLat = Math.max(LatLng.getLatitude(), maxLat);
maxLng = Math.max(LatLng.getLongitude(), maxLng);
markers.add(addMarker(LatLng));
cursor.moveToNext();
}
map.moveToCameraBounds(
new LatLngBounds(new LatLng(minLat, minLng), new LatLng(maxLat, maxLng)),
new ScreenBounds(
new ScreenPos(0, 0),
new ScreenPos(map.getWidth(), map.getHeight())
),
true, 0.25f);
}
}
این متد برای اضافهکردن نشانگر به نقشه استفاده میشود که در صفحات پیش، توضیح داده شده است.
// This method gets a LngLat as input and adds a marker on that position
private Marker addMarker(LatLng loc) {
// Creating animation for marker. We should use an object of type AnimationStyleBuilder, set
// all animation features on it and then call buildStyle() method that returns an object of type
// AnimationStyle
AnimationStyleBuilder animStBl = new AnimationStyleBuilder();
animStBl.setFadeAnimationType(AnimationType.ANIMATION_TYPE_SMOOTHSTEP);
animStBl.setSizeAnimationType(AnimationType.ANIMATION_TYPE_SPRING);
animStBl.setPhaseInDuration(0.5f);
animStBl.setPhaseOutDuration(0.5f);
AnimationStyle animSt = animStBl.buildStyle();
// Creating marker style. We should use an object of type MarkerStyleCreator, set all features on it
// and then call buildStyle method on it. This method returns an object of type MarkerStyle
MarkerStyleBuilder markStCr = new MarkerStyleBuilder();
markStCr.setSize(30f);
markStCr.setBitmap(BitmapUtils.createBitmapFromAndroidBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.ic_marker)));
// AnimationStyle object - that was created before - is used here
markStCr.setAnimationStyle(animSt);
MarkerStyle markSt = markStCr.buildStyle();
// Creating marker
Marker marker = new Marker(loc, markSt);
// Adding marker to map!
map.addMarker(marker);
return marker;
}
با هر بار انتخاب یا لغو انتخاب ToggleButton
موجود در رابط کاربری، این متد صدا زده میشود. در این متد بررسی میشود که اگر toggleButton
انتخاب شده باشد، متد getDBPoints
صدا زده شود و در غیر این صورت لایه پایگاه داده از مجموعه لایههای نقشه حذف شود.
public void toggleDatabaseLayer(View view) {
ToggleButton toggleButton = (ToggleButton) view;
if (toggleButton.isChecked())
getDBPoints();
else
for (Marker marker : markers) {
map.clearMarkers();
}
}