20 Şubat 2021 Cumartesi

Android Kütüphane Sistemi Mobil Uygulaması - 6 (Parametreler)

Merhabalar. Android Kütüphane Sistemi Mobil Uygulaması yazı dizisinin altıncı yazısı ile yeniden sizlerleyim. Bu yazıda sistemde ki parametrik değerlerin (kitap türü ve yayınevi) listelenişi ve kaydedilişinden bahsedeceğim.Tekrar belirtmekte fayda olacak, uygulamanın kodlarına buradan erişebilirsiniz.

Öncelikle parametre activity'sine sol taraftan parametre seçilerek gidilebiliyor. Bu kısımda  temel de BottomNavigation'u extend eden custom bir component kullandım. Parametre ekranının görüntüsü aşağıdaki gibidir.



Dilerseniz bu activity'nin layout dosyasına bakalım ve daha sonra da burada kullandığım custom component bahsine geçelim.

activity_parametre.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/parametreRootLayoutId"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">

<FrameLayout
android:id="@+id/parametreFragmentTutucu"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="90dp"
app:layout_constraintBottom_toTopOf="@+id/parametreBottomNavigation"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0">

</FrameLayout>

<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/parametreEkleFloatingButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="true"
app:layout_constraintEnd_toEndOf="parent"
android:backgroundTint="@color/colorAccent"
android:layout_marginTop="1dp"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/parametreFragmentTutucu"
app:srcCompat="@drawable/ic_add_24dp" />

<com.mesutemre.kutuphanesistemi.customcomponents.CurvedBottomNavigationView
android:id="@+id/parametreBottomNavigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">

</com.mesutemre.kutuphanesistemi.customcomponents.CurvedBottomNavigationView>

</androidx.constraintlayout.widget.ConstraintLayout>

Burada FrameLayout listeleri tutmakla görevli olup, FloatingActionButton ise parametre kayıt popup ımızı açmaktadır. Listeler arası geçiş ve NavigationBottom ise CurvedBottomNavigationView componenti ile sağlanmaktadır. Bu componentin kodları aşağıda ki gibidir.

CurvedBottomNavigationView.kt

class CurvedBottomNavigationView(context: Context, attrs: AttributeSet) :BottomNavigationView(context, attrs) {

private lateinit var mPath: Path;
private lateinit var mPaint: Paint;

private val CURVE_CIRCLE_RADIUS = 110 / 2;

private var mFirstCurveStartPoint: Point = Point();
private var mFirstCurveEndPoint: Point = Point();
private var mFirstCurveControlPoint1: Point = Point();
private var mFirstCurveControlPoint2: Point = Point();

private var mSecondCurveStartPoint: Point = Point();
private var mSecondCurveEndPoint: Point = Point();
private var mSecondCurveControlPoint1: Point = Point();
private var mSecondCurveControlPoint2: Point = Point();

private var mNavigationBarWidth: Int = 0;
private var mNavigationBarHeight: Int = 0;

init {
this.init();
}

private fun init(): Unit {
mPath = Path();
mPaint = Paint();
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
val colors = IntArray(3);
colors[0] = ContextCompat.getColor(
context,
R.color.bottom_start_color
);
colors[1] = ContextCompat.getColor(
context,
R.color.bottom_center_color
);
colors[2] = ContextCompat.getColor(
context,
R.color.bottom_end_color
);

val positions = FloatArray(3); //floatArrayOf(0f, 0.3f, 0.6f);
positions[0] = 0f;
positions[1] = 0.2f;
positions[2] = 0.4f;
mPaint.setShader(
LinearGradient(
0f, 0f, measuredWidth.toFloat(), 0f,
colors,
positions,
Shader.TileMode.CLAMP
)
);

mPaint.setShader(
LinearGradient(
0f, 0f, 0f, 250f,
colors, positions,
Shader.TileMode.MIRROR
)
);
setBackgroundColor(Color.TRANSPARENT);
}

override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh);

mNavigationBarWidth = getWidth();
mNavigationBarHeight = getHeight();

mFirstCurveStartPoint.set(
(mNavigationBarWidth / 2) - (CURVE_CIRCLE_RADIUS * 2) - (CURVE_CIRCLE_RADIUS / 3),
0
);
mFirstCurveEndPoint.set(
mNavigationBarWidth / 2,
CURVE_CIRCLE_RADIUS + (CURVE_CIRCLE_RADIUS / 4)
);
mSecondCurveStartPoint = mFirstCurveEndPoint;
mSecondCurveEndPoint.set(
(mNavigationBarWidth / 2) + (CURVE_CIRCLE_RADIUS * 2) + (CURVE_CIRCLE_RADIUS / 3),
0
);

mFirstCurveControlPoint1.set(
mFirstCurveStartPoint.x + CURVE_CIRCLE_RADIUS + (CURVE_CIRCLE_RADIUS / 4),
mFirstCurveStartPoint.y
);
// the coordinates (x,y) of the 2nd control point on a cubic curve
mFirstCurveControlPoint2.set(
mFirstCurveEndPoint.x - (CURVE_CIRCLE_RADIUS * 2) + CURVE_CIRCLE_RADIUS,
mFirstCurveEndPoint.y
);

mSecondCurveControlPoint1.set(
mSecondCurveStartPoint.x + (CURVE_CIRCLE_RADIUS * 2) - CURVE_CIRCLE_RADIUS,
mSecondCurveStartPoint.y
);
mSecondCurveControlPoint2.set(
mSecondCurveEndPoint.x - (CURVE_CIRCLE_RADIUS + (CURVE_CIRCLE_RADIUS / 4)),
mSecondCurveEndPoint.y
);

mPath.reset();
mPath.moveTo(0F, 0F);
mPath.lineTo(mFirstCurveStartPoint.x.toFloat(), mFirstCurveStartPoint.y.toFloat());

mPath.cubicTo(
mFirstCurveControlPoint1.x.toFloat(), mFirstCurveControlPoint1.y.toFloat(),
mFirstCurveControlPoint2.x.toFloat(), mFirstCurveControlPoint2.y.toFloat(),
mFirstCurveEndPoint.x.toFloat(), mFirstCurveEndPoint.y.toFloat()
);
mPath.cubicTo(
mSecondCurveControlPoint1.x.toFloat(), mSecondCurveControlPoint1.y.toFloat(),
mSecondCurveControlPoint2.x.toFloat(), mSecondCurveControlPoint2.y.toFloat(),
mSecondCurveEndPoint.x.toFloat(), mSecondCurveEndPoint.y.toFloat()
);

mPath.lineTo(mNavigationBarWidth.toFloat(), 0F);
mPath.lineTo(mNavigationBarWidth.toFloat(), mNavigationBarHeight.toFloat());
mPath.lineTo(0F, mNavigationBarHeight.toFloat());
mPath.close();
}

override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
super.onLayout(changed, left, top, right, bottom)
}


@SuppressLint("ResourceAsColor")
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas);
canvas?.drawPath(mPath, mPaint);
}
}

Burada NavigationBottom componenti radius şekil verilerek özelleştirilmiştir. Burada tanımlanan

    val colors = IntArray(3);

değişkeni CustomNavigationBottomView'ın dikeyde alacağı renklerin setlendiği değişkendir. Bu işlem LinearGradient nesnesi ile sağlanmaktadır. CURVE_CIRCLE_RADIUS değişkeninin değeri değiştirilerek radiusluk artırılabilir ya da azaltılabilir. Şimdi de activitye göz atalım.

ParametreActivity.kt

class ParametreActivity : AppCompatActivity() {

private lateinit var tempFragment: Fragment;

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_parametre);

val actionbar = supportActionBar;
actionbar!!.title = resources.getString(R.string.parametreActivityTitle);
actionbar.setDisplayHomeAsUpEnabled(true);

val pd: Dialog = Dialog(this);
val pdView: View = layoutInflater.inflate(R.layout.custom_progress_dialog,null);
pd.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT));
pd.setTitle(null);
pd.setContentView(pdView);
pd.setCancelable(false);

initNavBottomPreferences(parametreBottomNavigation);

supportFragmentManager.beginTransaction()?.replace(R.id.parametreFragmentTutucu,
YayineviListeFragment()
)?.commit();

parametreBottomNavigation.setOnNavigationItemSelectedListener { menuItem ->
if(menuItem.itemId == R.id.yayinEviItem){
tempFragment = YayineviListeFragment();
}else if(menuItem.itemId == R.id.kitapTurItem){
tempFragment = KitapturListeFragment();
}
supportFragmentManager.beginTransaction().replace(R.id.parametreFragmentTutucu,tempFragment)?.commit();
true;
}

parametreEkleFloatingButton.setOnClickListener {
openParametreEklemeDlg(this,pd);
}
}

override fun onSupportNavigateUp(): Boolean {
onBackPressed();
return true;
}

private fun initNavBottomPreferences(navBottomMenu: CurvedBottomNavigationView){
navBottomMenu.inflateMenu(R.menu.parametre_menu_nav_items);
navBottomMenu.labelVisibilityMode = LabelVisibilityMode.LABEL_VISIBILITY_LABELED;
navBottomMenu.menu.getItem(1).isVisible = false;
}

private fun openParametreEklemeDlg(c: Context, pd: Dialog) {
val view:View = layoutInflater.inflate(R.layout.dialog_parametre_ekleme,null);
val alertDialogBuilder = AlertDialog.Builder(this)
.setTitle(R.string.paramEkleDlgTitle)
.setView(view)
.setPositiveButton(R.string.kaydetLabel, null)
.setNegativeButton(R.string.iptalLabel, null)
.show();
val paramTipCombo: Spinner = view.findViewById(R.id.paramTypeSpinner) as Spinner;
val paramAciklama: EditText = view.findViewById(R.id.paramText) as EditText;
val paramTipArr = c.resources.getStringArray(R.array.parametreTipArr);
val adapter = ArrayAdapter(alertDialogBuilder.context,android.R.layout.simple_list_item_1,paramTipArr);
paramTipCombo.adapter = adapter;

val kaydetButton: Button = alertDialogBuilder.getButton(AlertDialog.BUTTON_POSITIVE);
kaydetButton.setOnClickListener {
val selectedParamTip = paramTipCombo.selectedItem;
val aciklama = paramAciklama.text.toString();

if(selectedParamTip.equals(resources.getString(R.string.paramTipSeciniz))){
SimpleToast.warning(c, resources.getString(R.string.parametreSecinizUyari), "{fa-exclamation-triangle}");
return@setOnClickListener;
}

if(aciklama.isEmpty()){
SimpleToast.warning(c, resources.getString(R.string.aciklamaBosUyari), "{fa-exclamation-triangle}");
return@setOnClickListener;
}

val jsonObj: JSONObject = JSONObject();
jsonObj.put("aciklama",aciklama);
val parametreService: IParametreService = WebApiUtil.getParametreService(c);

pd.show();

if(selectedParamTip.equals(resources.getString(R.string.paramKitapTur))){
kitapTurKaydet(parametreService,pd,jsonObj.toString());
alertDialogBuilder.dismiss();
pd.dismiss();
}else if(selectedParamTip.equals(resources.getString(R.string.paramYayinevi))){
yayinEviKaydet(parametreService,pd,jsonObj.toString());
alertDialogBuilder.dismiss();
}
}

val iptalButton: Button = alertDialogBuilder.getButton(AlertDialog.BUTTON_NEGATIVE);
iptalButton.setOnClickListener {
alertDialogBuilder.dismiss();
}
}

private fun kitapTurKaydet(parametreService: IParametreService,pd:Dialog,jsonStr:String){
parametreService.kitapTurKaydet(jsonStr).enqueue(object:
Callback<ResponseStatusModel> {
override fun onFailure(call: Call<ResponseStatusModel>?, t: Throwable) {
SimpleToast.error(applicationContext, resources.getString(R.string.kitapTurSilmeHata), "{fa-times-circle}");
pd.dismiss();
}

override fun onResponse(call: Call<ResponseStatusModel>?, response: Response<ResponseStatusModel>) {
if(response.body() != null){
val respModel = response.body() as ResponseStatusModel;
SimpleToast.info(applicationContext, respModel.statusMessage, "{fa-check}");
supportFragmentManager.beginTransaction().replace(R.id.parametreFragmentTutucu,KitapturListeFragment())?.commit();
pd.dismiss();
}
}
});
}

private fun yayinEviKaydet(parametreService: IParametreService,pd:Dialog,jsonStr:String){
parametreService.yayinEviKaydet(jsonStr).enqueue(object:
Callback<ResponseStatusModel> {
override fun onFailure(call: Call<ResponseStatusModel>?, t: Throwable) {
SimpleToast.error(applicationContext, resources.getString(R.string.yayinEviSilmeHata), "{fa-times-circle}");
pd.dismiss();
}

override fun onResponse(call: Call<ResponseStatusModel>?, response: Response<ResponseStatusModel>) {
if(response.body() != null){
val respModel = response.body() as ResponseStatusModel;
SimpleToast.info(applicationContext, respModel.statusMessage, "{fa-check}");
pd.dismiss();
supportFragmentManager.beginTransaction().replace(R.id.parametreFragmentTutucu,YayineviListeFragment())?.commit();
}
}
});
}
}

Burada initNavBottomPreferences metodu ile bottom navigation un menüleri de setlenmiştir. 

parametre_menu_nav_items.xml

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">

<item
android:id="@+id/yayinEviItem"
android:icon="@drawable/ic_format_list_numbered_24dp"
app:showAsAction="ifRoom"
android:title="@string/yayinEvleriItemLabel"/>

<item
android:id="@+id/sepet"
app:showAsAction="never"
android:title=""/>

<item
android:id="@+id/kitapTurItem"
android:icon="@drawable/ic_format_list_numbered_24dp"
app:showAsAction="ifRoom"
android:title="@string/kitapTurleriItemLabel"/>

</menu>

Menüde ki kitapTurItem ve yayinEviItem itemlerı ile kitap türleri ya da yayın evleri listelerine ulaşılmaktadır.

Bu listeler fragment olup FrameLayout üzerinde gösterilmektedir. Bu fragmentlara (KitapturListeFragment ve YayineviListeFragment) buradan erişebilirsiniz.

FloatingActionButton ile parametre kayıt popup ı açılıyor demiştik. Bu işlem aşağıda ki metodda olmaktadır.

private fun openParametreEklemeDlg(c: Context, pd: Dialog) {
val view:View = layoutInflater.inflate(R.layout.dialog_parametre_ekleme,null);
val alertDialogBuilder = AlertDialog.Builder(this)
.setTitle(R.string.paramEkleDlgTitle)
.setView(view)
.setPositiveButton(R.string.kaydetLabel, null)
.setNegativeButton(R.string.iptalLabel, null)
.show();
val paramTipCombo: Spinner = view.findViewById(R.id.paramTypeSpinner) as Spinner;
val paramAciklama: EditText = view.findViewById(R.id.paramText) as EditText;
val paramTipArr = c.resources.getStringArray(R.array.parametreTipArr);
val adapter = ArrayAdapter(alertDialogBuilder.context,android.R.layout.simple_list_item_1,paramTipArr);
paramTipCombo.adapter = adapter;

val kaydetButton: Button = alertDialogBuilder.getButton(AlertDialog.BUTTON_POSITIVE);
kaydetButton.setOnClickListener {
val selectedParamTip = paramTipCombo.selectedItem;
val aciklama = paramAciklama.text.toString();

if(selectedParamTip.equals(resources.getString(R.string.paramTipSeciniz))){
SimpleToast.warning(c, resources.getString(R.string.parametreSecinizUyari), "{fa-exclamation-triangle}");
return@setOnClickListener;
}

if(aciklama.isEmpty()){
SimpleToast.warning(c, resources.getString(R.string.aciklamaBosUyari), "{fa-exclamation-triangle}");
return@setOnClickListener;
}

val jsonObj: JSONObject = JSONObject();
jsonObj.put("aciklama",aciklama);
val parametreService: IParametreService = WebApiUtil.getParametreService(c);

pd.show();

if(selectedParamTip.equals(resources.getString(R.string.paramKitapTur))){
kitapTurKaydet(parametreService,pd,jsonObj.toString());
alertDialogBuilder.dismiss();
pd.dismiss();
}else if(selectedParamTip.equals(resources.getString(R.string.paramYayinevi))){
yayinEviKaydet(parametreService,pd,jsonObj.toString());
alertDialogBuilder.dismiss();
}
}

val iptalButton: Button = alertDialogBuilder.getButton(AlertDialog.BUTTON_NEGATIVE);
iptalButton.setOnClickListener {
alertDialogBuilder.dismiss();
}
}

Burada validation uyarılarını SimpleToast kütüphanesi ile vermekteyim. Bu kütüphaneyi kullanmak için build.gradle (Module:App) dosyanıza aşağıda ki dependency i eklemeniz gerekmektedir.

    implementation 'com.github.Pierry:SimpleToast:v1.7'

Burada ki kayıt popup ekranı aşağıda ki gibidir.



Bu popup ın layout dosyası aşağıda ki gibidir.

dialog_parametre_ekleme.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content">

<Spinner
android:id="@+id/paramTypeSpinner"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:layout_marginTop="35dp"
android:layout_marginEnd="5dp"
style="@style/spinner_style"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<EditText
android:id="@+id/paramText"
android:layout_width="0dp"
android:layout_height="45dp"
android:layout_marginStart="5dp"
android:layout_marginTop="40dp"
android:layout_marginEnd="5dp"
android:background="@drawable/edit_text_border"
android:ems="10"
android:hint="@string/paramAciklamaHint"
android:inputType="textPersonName"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/paramTypeSpinner" />

</androidx.constraintlayout.widget.ConstraintLayout>

Soru,istek ve önerilerinizi mesutemrecelenk@gmail.com adresine yazabilirsiniz. Bir sonraki yazıda görüşmek üzere hoşça kalın...





Hiç yorum yok:

Yorum Gönder