พื้นฐาน Android - การแชร์ข้อมูลไปยัง Facebook บน Android App

ฟังก์ชั่นการแชร์

private fun shareText2Facebook(){
    val intent = Intent()
    intent.action = Intent.ACTION_SEND
    intent.putExtra(Intent.EXTRA_TEXT, "hello world")
    intent.type = "text/plain"
    intent.setPackage("com.facebook.orca")  // เปลี่ยนเป็น package อื่นๆได้
    startActivity(intent)
}


ลองแชร์ลิงค์ PlayStore

https://play.google.com/store/apps/details?id=com.facebook.katana


ผลลัพธ์ที่ได้ในมุมมองมือถือ





ในมุมมองของโน๊ตบุ๊ค



หากจำเป็นต้องแชร์แบบใช้โมเดลเนื้อหา (Advance)

- ลิงก์
- รูปภาพ
- วิดีโอ
- มัลติมีเดีย (การผสมผสานทั้งรูปภาพและวิดีโอ)

ศึกษาได้จากที่นี่ครับ : https://developers.facebook.com/docs/sharing/android



Read more ...

การติดตั้งและนำ google material design มาใช้ในโปรเจค android app



การนำมาใช้ร่วมกับ Theme.AppCompat (Default Theme)

Required
*update your app's compileSdkVersion to 28 
*download the Android 9 using the SDK manager.


Step 1 - Setting for Library 

dependencies {
    implementation 'com.google.android.material:material:1.0.0'  // เลือกใช้ตามเวอร์ชั่นที่ releases ใน github
    ..
    ..
    ..
  }

Step 2 - Add Material Components to style.xml

Theme ของ meterial design ที่มีให้ใช้

Theme.MaterialComponents
Theme.MaterialComponents.NoActionBar
Theme.MaterialComponents.Light
Theme.MaterialComponents.Light.NoActionBar
Theme.MaterialComponents.Light.DarkActionBar
Theme.MaterialComponents.DayNight
Theme.MaterialComponents.DayNight.NoActionBar
Theme.MaterialComponents.DayNight.DarkActionBar


เราสามารถเพิ่ม Component เข้ามาทีละตัวได้ โดยไม่เพิ่มธีมของแอพ
ทำให้รักษาเค้าโครงเดิมที่มีอยู่ทำงานเหมือนเดิม
อยากใช้อันไหนเลือกจับมาใส่ทีละตัวได้ครับ ชีวิตเริ่มสดใสขึ้นมานิดนึง >_<


/res/values/style.xml
<style name="Theme.MyApp" parent="Theme.AppCompat">

  <!-- Original AppCompat attributes. -->
  <item name="colorPrimary">@color/my_app_primary_color</item>
  <item name="colorSecondary">@color/my_app_secondary_color</item>
  <item name="android:colorBackground">@color/my_app_background_color</item>
  <item name="colorError">@color/my_app_error_color</item>

  <!-- New MaterialComponents attributes. -->
  <item name="colorPrimaryVariant">@color/my_app_primary_variant_color</item>
  <item name="colorSecondaryVariant">@color/my_app_secondary_variant_color</item>
  <item name="colorSurface">@color/my_app_surface_color</item>
  <item name="colorOnPrimary">@color/my_app_color_on_primary</item>
  <item name="colorOnSecondary">@color/my_app_color_on_secondary</item>
  <item name="colorOnBackground">@color/my_app_color_on_background</item>
  <item name="colorOnError">@color/my_app_color_on_error</item>
  <item name="colorOnSurface">@color/my_app_color_on_surface</item>
  <item name="scrimBackground">@color/mtrl_scrim_color</item>
  <item name="textAppearanceHeadline1">@style/TextAppearance.MaterialComponents.Headline1</item>
  <item name="textAppearanceHeadline2">@style/TextAppearance.MaterialComponents.Headline2</item>
  <item name="textAppearanceHeadline3">@style/TextAppearance.MaterialComponents.Headline3</item>
  <item name="textAppearanceHeadline4">@style/TextAppearance.MaterialComponents.Headline4</item>
  <item name="textAppearanceHeadline5">@style/TextAppearance.MaterialComponents.Headline5</item>
  <item name="textAppearanceHeadline6">@style/TextAppearance.MaterialComponents.Headline6</item>
  <item name="textAppearanceSubtitle1">@style/TextAppearance.MaterialComponents.Subtitle1</item>
  <item name="textAppearanceSubtitle2">@style/TextAppearance.MaterialComponents.Subtitle2</item>
  <item name="textAppearanceBody1">@style/TextAppearance.MaterialComponents.Body1</item>
  <item name="textAppearanceBody2">@style/TextAppearance.MaterialComponents.Body2</item>
  <item name="textAppearanceCaption">@style/TextAppearance.MaterialComponents.Caption</item>
  <item name="textAppearanceOverline">@style/TextAppearance.MaterialComponents.Overline</item>
  <item name="textAppearanceButton">@style/TextAppearance.MaterialComponents.Button</item>
  ^
  ^
  // เราจะมาลองใช้ textAppearanceButton กัน 
    ปล.ถ้าลบออก จะไม่สามารถกำหนด Style ของ Meterial ให้กับ Button ได้นะ
..
..
</style>



Step 3 - เรียกใช้ Components ใน layout ไฟล์ของเราได้เลย

activity_main.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:gravity="center"
    android:orientation="vertical">

    <com.google.android.material.button.MaterialButton
        style="@style/Widget.MaterialComponents.Button"
        android:layout_margin="10dp"
        android:id="@+id/material_button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Default"/>


    <com.google.android.material.button.MaterialButton
        style="@style/Widget.MaterialComponents.Button.UnelevatedButton"
        android:layout_margin="10dp"
        android:id="@+id/material_button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Unelevated Button"/>


    <com.google.android.material.button.MaterialButton
        style="@style/Widget.MaterialComponents.Button.OutlinedButton"
        android:layout_margin="10dp"
        android:id="@+id/material_button3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Outlined Button"/>


    <com.google.android.material.button.MaterialButton
        style="@style/Widget.MaterialComponents.Button.TextButton"
        android:layout_margin="10dp"
        android:id="@+id/material_button4"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Text Button"/>

    <com.google.android.material.textfield.TextInputLayout
        style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="Label of TextInputLayout">

        <com.google.android.material.textfield.TextInputEditText
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
    </com.google.android.material.textfield.TextInputLayout>

</LinearLayout>


output :



จากด้านบนถ้าเราตัด Style ของ TextInputLayout ออก

<com.google.android.material.textfield.TextInputLayout
    style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:hint="Label of TextInputLayout">

TextInputLayout ก็จะกลายเป็นแบบภาพด้านล่างนี้แทนนะครับ
เส้นขอบรอบๆ Edittext ของเรามาจาก style โดยไม่ต้องเขียนโค้ดแต่ง Style เอาเอง >_<




สุดท้ายครับ ขอยกตัวอย่างอีกเคสเรื่อง CardView
กรณีเราเปิด Tag และใช้ Style มาเป็นของ MeterialCardView แบบนี้
จะมี strokeColor , strokeWidth และอื่นๆเพิ่มมาให้เราใช้งานด้วยครับ


<com.google.android.material.card.MaterialCardView
    style="@style/Widget.MaterialComponents.CardView"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    android:layout_margin="10dp"
    android:layout_weight="1"
    app:strokeColor="@color/colorPrimaryDark"
    app:strokeWidth="2dp"
    app:cardBackgroundColor="#DAD9D9"
    app:cardCornerRadius="20dp">



สรุปขั้นตอนการเริ่มใช้งาน

  1. Setting for Library
  2. Add Material Components to style.xml
  3. เรียกใช้ Components ใน layout ไฟล์ของเราได้เลย


*Tip - กรณีไม่สามารถ inherit หรือใช้ theme โดยตรงจาก Material Components theme ได้
เราสามารถ inherit จาก Components Bridge theme ได้มี Bridge themes ให้เลือกใช้ดังนี้ครับ

  • Theme.MaterialComponents.Bridge
  • Theme.MaterialComponents.Light.Bridge
  • Theme.MaterialComponents.NoActionBar.Bridge
  • Theme.MaterialComponents.Light.NoActionBar.Bridge
  • Theme.MaterialComponents.Light.DarkActionBar.Bridge

Ref :
https://github.com/material-components/material-components-android/blob/master/docs/getting-started.md
Read more ...

การสร้าง Custom View จาก Layout XML ( รวม 2 TextView เป็น 1 InfoCardView )

(บทความนี้ผมสรุปจาก http://www.akexorcist.com/2016/12/introduction-to-custom-view-part-2.html )
ไม่ได้เขียนขึ้นเองครับ ใครอยากอ่านรายละเอียดเต็มๆตามไปอ่านลิงค์ ^ ได้เลยครับ

สรุป วิธีนี้ก็คือต้องการสร้างเป็น Custom View ตัวใหม่ขึ้นมา
โดยมี View อยู่ข้างในหลายๆตัว เพื่อให้ควบคุมสั่งงานจาก Custom View ตัวเดียวไปเลย
แทนที่จะไปนั่งควบคุม View แต่ละตัวให้โค้ดมันวุ่นวาย




Example : InfoCardView


widget_info_card.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="wrap_content"
    android:orientation="vertical">

    <TextView
        android:id="@+id/tv_title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#ff7200"
        android:padding="16dp"
        android:textColor="#ffffff"
        android:textSize="18sp" />

    <TextView
        android:id="@+id/tv_content"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#ffffff"
        android:padding="16dp"
        android:textColor="#333333"
        android:textSize="16sp" />

</LinearLayout>



สร้าง Custom View สำหรับ InfoCardView

แนะนำว่าให้สืบทอดคลาสจาก FrameLayout
เพราะว่าการสร้าง Custom View ด้วยวิธีนี้คือการสร้าง Layout XML ที่ต้องการ
แล้วเอามาแปะใน Custom View ดังนั้น FrameLayout จึงเหมาะสมที่สุด

InfoCardView.kt

class InfoCardView : FrameLayout, View.OnClickListener {

    var tvTitle : TextView? = null
    var tvContent : TextView? = null
    private var infoClickListener: InfoClickListener? = null

    constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
        setup()
    }

    constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
        setup()
    }


         private fun setup() {
        inflate(context, R.layout.widget_info_card, this)
        bindView()
        setupView()
    }

    private fun bindView() {
        tvTitle = findViewById(R.id.tv_title)
        tvContent = findViewById(R.id.tv_content)

    }

    private fun setupView() {
        tvTitle?.setOnClickListener(this)
        tvContent?.setOnClickListener(this)

    }



     // สร้างฟังก์ชั่นไว้รอใช้งานใน MainActivity.kt
    fun setTitle(title: String) {
        tvTitle?.text = title
    }
    fun setContent(content: String) {
        tvContent?.text = content
    }
    fun setInfoClickListener(listener: InfoClickListener) {
        this.infoClickListener = listener
    }

    // ดักการคลิกของผู้ใช้ใน layout file : widget_info_card.xml
    override fun onClick(view: View) {
        if (view === tvTitle) {
            onTitleClick()
        } else if (view === tvContent) {
            onContentClick()
        }
    }


     // สร้าง Listener Onclick ให้ View
    private fun onTitleClick() {
        if (infoClickListener != null) {
            infoClickListener!!.onTitleClick()
        }
    }
    private fun onContentClick() {
        if (infoClickListener != null) {
            infoClickListener!!.onContentClick()
        }
    }


    // สร้าง interface ไว้ใช้งานใน MainActivity.kt     interface InfoClickListener {
        fun onTitleClick()
        fun onContentClick()
    }

}



MainActivity.kt

class MainActivity : AppCompatActivity(), InfoCardView.InfoClickListener {   // Implement  InfoClickListener interface

    private var icvAndroidVersion : InfoCardView? = null

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

        bindView()
        setupView()
    }


    private fun bindView() {
        icvAndroidVersion = findViewById<InfoCardView>(R.id.icv_android_version)
    }
    // เรียกใช้งาน ฟังก์ชั่นใน  InfoCardView.kt
    private fun setupView() {
        icvAndroidVersion?.setTitle(getString(R.string.nougat_title))
        icvAndroidVersion?.setContent(getString(R.string.nougat_content))
        icvAndroidVersion?.setInfoClickListener(this)
    }


    // แสดง Pop up เมื่อคลิกที่ View : Title     
    override fun onTitleClick() {
        Toast.makeText(this, "onTitleClick", Toast.LENGTH_LONG).show()
    }

     // แสดง Pop up เมื่อคลิกที่ View : Content     
     override fun onContentClick() {
        Toast.makeText(this, "onContentClick", Toast.LENGTH_LONG).show()
    }

}


ลองเรียกใช้งานเพื่อดูผลลัพธ์การทำงาน

<com.example.trymycustomview.InfoCardView
      android:id="@+id/icv_android_version"
      android:layout_width="match_parent"
      android:layout_height="wrap_content" />


อ่านต้นฉบับ + รายละเอียดแบบเต็มๆ
http://www.akexorcist.com/2016/12/introduction-to-custom-view-part-2.html


Read more ...

สรุปภาพรวมเรื่อง Custom View บน Android App หลายๆแบบ

Custom View แบบแก้ไข View ตัวเดียว

Custom จาก Properties เดิมที่มีอยู่แล้ว (อยู่ด้านล่าง)
CustomView แบบ extends widgets class ที่มีอยู่แล้วเช่น Button , TextView ()
CustomView แบบ extends View Class (อยู่ด้านล่าง)

Custom View แบบรวมหลายๆ View ให้เป็นตัวเดียวกัน

- CustomView ขึ้นมาใหม่โดย extends LinearLayout (อยู่ด้านล่าง)
- Custom Adapter ของ ListView
- Custom Adapter ของ GridView
- Custom Adapter ของ Recycler View : https://trymydroid.blogspot.com/2019/07/android-kotlin-json-parsing-using.html

(Advance)
ลองทำ Custom Toast จากโค้ด Kotlin , Java
ลองทำ Custom Dialog จากโค้ด Kotlin , Java

_________________________________________________
Custom จาก Properties เดิมที่มีอยู่แล้ว

Example การเปลี่ยน Icon ของ SeekBar

<SeekBar
            android:id="@+id/simpleSeekBar"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:max="200"
            android:progress="10"
            android:theme="@style/SeekBarColor"
            android:thumb="@drawable/ic_action_music_1"/>

ตรง drawble นอกจาก icon หรือรูปภาพ เราสามารถสร้างรูปทรงขึ้นมาใส่เองได้ (Drawable Resource)




Custom View ขึ้นมาใหม่โดย extends LinearLayout

- สร้างหน้าตาของ View เช่น BackButton - ImageView - NextButton ( ใน custom_image_slide.xml )

- สร้าง Class ชื่อ CustomImageSlide ที่ extends LinearLayout (Other : FrameLayout , RelativeLayout)
  เพื่อ inflate : custom_image_slide.xml เข้ามา
  เพื่อควบคุมว่ากดปุ่ม Next , Back ให้เปลี่ยนภาพไปมาได้

- นำ CustomImageSlide ไปใช้งานใน layout ของ activity ที่ต้องการ

เช่น
<com.example.trymydroid.CustomImageSlide
        android:id="@+id/imageSlide"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

ไอเดียร์นี้มาจาก บล็อคพี่สะมะเกรียด
อ่านเต็มๆได้ที่ : http://www.akexorcist.com/2016/12/introduction-to-custom-view-part-2.html

หรืออยากเอาพอเป็นกระไส เรียกน้ำย่อยก่อนได้ที่
http://trymydroid.blogspot.com/2019/10/custom-view-layout-xml-2-textview-1.html





CustomView แบบ extends View Class

ตัวอย่างนี้ค่อนข้าง Advance เพราะจะเป็นการสร้าง View ขึ้นมาใหม่ใน onDraw
และ Advance ขึ้นไปอีกคือการทำ Touch Listener ให้ View ตัวนี้ (ไม่มีในบทความนี้)

Example :

class TestCustomViewDraw1(context: Context, attrs: AttributeSet) : View(context, attrs){

    @SuppressLint("DrawAllocation")
    override fun onDraw(canvas: Canvas) {
        // call the super method to keep any drawing from the parent side.
        super.onDraw(canvas)

        val paint =  Paint(Paint.ANTI_ALIAS_FLAG)
        paint.color = Color.BLACK
        paint.style = Paint.Style.STROKE
        paint.strokeWidth = 2F

        canvas.drawLine(0F, 0F ,100F ,100F, paint)  // drawLine(startX, startY, stopX, stopY)
    }
}

other :
canvas.drawCircle(....)
canvas.drawRect(....)
canvas.drawRoundRect(....)


ref :
http://nfcandroid.blogspot.com/2013/03/android-basic-custom-view.html
https://blog.mindorks.com/create-your-own-custom-view

Read more ...

การสร้าง Custom Toolbar + ButtomNavigationBar

โดยปกติ Toolbar เราจะปรับแต่งอะไรไม่ได้มาก
เราเลยจะมาลอง Custom Toolbar ขึ้นมาใหม่ BottomNavigationView
หน้าตาที่จะได้ประมาณนี้



build.gradle (Project : ...............)

allprojects {
    repositories {
        google()
        jcenter()
        maven { url "https://jitpack.io" }
    }
}


build.gradle (Module: app)

dependencies {
    ..
    ..
    implementation 'com.google.android.material:material:1.0.0'
    implementation 'com.github.mancj:MaterialSearchBar:0.8.2'
}

ต่อมาสร้าง BasicActivity ขึ้นมาในไฟล์ layout ของ Activity
จะมี AppBarLayout ที่มี Toolbar อยู่ด้านใน







แบ่งออกเป็น 2 ส่วนมี : AppBarLayout และ  BottomNavigationView

res/layout/layout_activity.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">


<com.google.android.material.appbar.AppBarLayout
    android:layout_height="wrap_content"
    android:layout_width="match_parent"
    android:padding="10dp"
    android:background="#008A7C"
    android:layout_gravity="top">

    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <TextView
            android:layout_marginTop="10dp"
            android:text="TryMyDroid"
            android:textColor="@color/white"
            android:textSize="20dp"
            android:layout_weight="1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>

        <com.mancj.materialsearchbar.MaterialSearchBar
            android:id="@+id/searchBar"
            style="@style/MaterialSearchBarLight"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="5"
            app:mt_hint="ชื่อสินค้า"
            app:mt_maxSuggestionsCount="10"
            app:mt_placeholder="ค้นหา"
            app:mt_placeholderColor="#008A7C" />

        <ImageView
            android:id="@+id/leftTopButton"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_weight="0.5"
            android:src="@drawable/ic_menu" />

    </LinearLayout>
</com.google.android.material.appbar.AppBarLayout>


// Content 


<com.google.android.material.bottomnavigation.BottomNavigationView
    android:background="@color/colorPrimary"
    app:itemIconTint="@color/white"
    app:itemTextColor="@color/white"
    android:layout_gravity="bottom"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:menu="@menu/bottom_nav_menu"/>

</androidx.coordinatorlayout.widget.CoordinatorLayout>


res/menu/buttom_nav_menu.xml

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id="@+id/navigation_home"
        android:icon="@drawable/ic_home"
        android:title="@string/title_home" />
    <item
        android:id="@+id/navigation_dashboard"
        android:icon="@drawable/ic_dashboard_black_24dp"
        android:title="@string/title_dashboard" />
    <item
        android:id="@+id/navigation_notifications"
        android:icon="@drawable/ic_notifications_black_24dp"
        android:title="@string/title_notifications" />
</menu>



Step ต่อไปเมื่อผู้ใช้คลิกไอคอนใน BottomNavigationView
ลองนำ Fragment มาใช้เป็นแต่ละหน้าดูครับ
.
ปล.ความจริงส่วนที่เป็น Navigation ควรถูกสร้างขึ้นก่อนนะครับ
แต่ใน Android Studio 3.5 ที่ผมใช้อยู่ BottomNavigation Activity วางโครงสร้างโค้ดมาเป็น MVVM
ซึ่ง ณ.ตอนนี้บอกตรงๆว่ายังไม่ชำนาญพอ เพราะยังเป็นของใหม่สำหรับผม
จึงขอจบบทความเลยแล้วกันครับ 555
.
.
รู้เมื่อไหร่ก็จะมาแชร์ๆกันในบล็อคนี้แหละครับ ฝากติดตามครับ ^^

Read more ...