Functional specifications

In the course of mobile development in this semester, I chose to make an application similar to douban, which provides users with ratings and records of book, video and audio games, and removes redundant social modules such as “group” in Douban.

⌈arkk⌋ from “Noah’s Ark”, meaning the retention of meaningful things/information to the user’s personal world. Double the k, double the flavor.

Divided into five modules:

  • STREAM – dynamic flow
  • ARCHIVE- Book, video, and tour files
  • NEW- NEW release/tag
  • The NOTIFICATIONS – notification
  • ME- User home page

Key source

JAVA file

fragment

package com.hzl.arkk;

import android.os.Bundle;

import androidx.fragment.app.Fragment;

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;


public class fragArchive extends Fragment {

    public fragArchive(a) {}@Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

        return inflater.inflate(R.layout.fragment_archive, container, false); }}Copy the code

MainActivity

package com.hzl.arkk;

import androidx.appcompat.app.AppCompatActivity;

import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;

import android.annotation.SuppressLint;
import android.os.Bundle;

import android.view.View;

import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;


public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private Fragment[] frags;
    private FragmentManager fragManager;

    private int selected = 0; // The interface is currently selected
    private int last = 1; // Go to the previous interface

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // First run execution
        if (savedInstanceState == null) {
            LinearLayout llStream = findViewById(R.id.llStream);
            LinearLayout llArchive = findViewById(R.id.llArchive);
            Button btnNew = findViewById(R.id.btnNew);
            LinearLayout llDM = findViewById(R.id.llDM);
            LinearLayout llUser = findViewById(R.id.llUser);
            // Set the listener
            llStream.setOnClickListener(this);
            llArchive.setOnClickListener(this);
            btnNew.setOnClickListener(this);
            llDM.setOnClickListener(this);
            llUser.setOnClickListener(this);

            initFrags();

            // Default home page
            llStream.performClick();
            this.setTitle("arkk"); }}/* Fragment initialization */
    private void initFrags(a) {
        frags = new Fragment[5];
        fragManager = getSupportFragmentManager();
        FragmentTransaction fragTrans = fragManager.beginTransaction();

        fragStream fragStream = new fragStream();
        fragArchive fragArchive = new fragArchive();
        fragNew fragNew = new fragNew();
        fragDM fragDM = new fragDM();
        fragUser fragUser = new fragUser();

        frags[0] = fragStream;
        frags[1] = fragArchive;
        frags[2] = fragNew;
        frags[3] = fragDM;
        frags[4] = fragUser;
        // Hide all fragments initially
        for(Fragment frag : frags) { fragTrans.hide(frag); }}/* Replace the fragment content */
    private void switchContent(Fragment former, Fragment latter) {
        FragmentTransaction fragTrans = fragManager.beginTransaction();
        if(former ! = latter) {if(! latter.isAdded()) { fragTrans.add(R.id.fragContainer, latter).hide(former).show(latter).commit(); }else{ fragTrans.hide(former).show(latter).commit(); }}}@SuppressLint("NonConstantResourceId")
    @Override
    public void onClick(View view) {
        // The focus color of the previous page is restored
        ImageView imvIcon = null;
        TextView txvTitle = null;
        switch (last) {
            case 0:
                imvIcon = findViewById(R.id.svgStream);
                txvTitle = findViewById(R.id.txtStream);
                break;
            case 1:
                imvIcon = findViewById(R.id.svgArchive);
                txvTitle = findViewById(R.id.txtArchive);
                break;
            case 3:
                imvIcon = findViewById(R.id.svgDM);
                txvTitle = findViewById(R.id.txtDM);
                break;
            case 4:
                imvIcon = findViewById(R.id.svgUser);
                txvTitle = findViewById(R.id.txtUser);
                break;
            default:
                break;
        }
        if(last ! =2) { // Exclude the "new" button in the middle
            imvIcon.setColorFilter(getColor(R.color.white));
            txvTitle.setTextColor(getColor(R.color.white));
        }
        // Determine the currently selected interface
        switch (view.getId()) {
            case R.id.llStream:
                this.setTitle("STREAM");// Change the actionBar title
                selected = 0;
                break;
            case R.id.llArchive:
                this.setTitle("ARCHIVE");
                selected = 1;
                break;
            case R.id.btnNew: // Hide actionBar when "New" button is selected
                getSupportActionBar().hide();
                selected = 2;
                break;
            case R.id.llDM:
                this.setTitle("NOTIFICATIONS");
                selected = 3;
                break;
            case R.id.llUser:
                this.setTitle("ME");
                selected = 4;
                break;
            default:
                break;
        }
        // The current selection is not "New" button and the actionBar is hidden
        if(selected ! =2 && !getSupportActionBar().isShowing()) {
            getSupportActionBar().show();
        }
        // Currently selected page color focus
        switch (selected) {
            case 0:
                imvIcon = findViewById(R.id.svgStream);
                txvTitle = findViewById(R.id.txtStream);
                break;
            case 1:
                imvIcon = findViewById(R.id.svgArchive);
                txvTitle = findViewById(R.id.txtArchive);
                break;
            case 3:
                imvIcon = findViewById(R.id.svgDM);
                txvTitle = findViewById(R.id.txtDM);
                break;
            case 4:
                imvIcon = findViewById(R.id.svgUser);
                txvTitle = findViewById(R.id.txtUser);
                break;
            default:
                break;
        }
        if(selected ! =2) { imvIcon.setColorFilter(getColor(R.color.purple_500)); txvTitle.setTextColor(getColor(R.color.purple_700)); } switchContent(frags[last], frags[selected]); last = selected; }}Copy the code

RES file

drawable

<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="200dp"
    android:height="200dp"
    android:viewportWidth="1024"
    android:viewportHeight="1024">
  <path
      android:pathData="M1024,912a32,32 0,0 1,-32 32L32,944a32,32 0,0 1, -32-32v-480a31 0.48,0.32A31.36,31.36 0,0, 1,256 80h512a31.36,31.36 0,0,25.6 13.98l0.48,-0.32 224, 320-0.48,0.32 a31.1.2,31.2 0,0, 1,6.4 18.02v480zm64, 880h896v-416h-205.891l-86.66, 144.48-0.48, -0.29a31.52,31.52 0,0,1,640 624h-256a31.52,31.52 0,0,1,-26.98 15.81 l - 0.45 and 0.29 L269.89, 464 l64, 464 v416zm751. 33144 L272.67, 144 l - 179.2, 256 l288, 400 a31. 52,26.98,31.52 0, 0 1 L0.48 15.81, 0.26, 86.66 144.45 h219.78 l86.69, 144.45 0.45, 0.26 A31.52, 53 z 31.52 0, 0, 1736 400 h194."
      android:fillColor="#FFFFFF"/>
</vector>
Copy the code

layout

activity_main


      
<androidx.constraintlayout.widget.ConstraintLayout 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:id="@+id/main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <FrameLayout
        android:id="@+id/fragContainer"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:orientation="horizontal"
        app:layout_constraintBottom_toTopOf="@+id/naviBar"
        app:layout_constraintEnd_toEndOf="@id/naviBar"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">

    </FrameLayout>

    <LinearLayout
        android:id="@+id/naviBar"
        android:layout_width="match_parent"
        android:layout_height="56dp"
        android:background="@color/purple_200"
        android:orientation="horizontal"
        app:layout_constraintBottom_toBottomOf="parent">

        <LinearLayout
            android:id="@+id/llStream"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:orientation="vertical"
            tools:ignore="UseCompoundDrawables">

            <ImageView
                android:id="@+id/svgStream"
                android:layout_width="24dp"
                android:layout_height="24dp"
                android:layout_gravity="center"
                android:layout_marginTop="8dp"
                app:srcCompat="@drawable/btm_stream" />

            <TextView
                android:id="@+id/txtStream"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:layout_marginBottom="12dp"
                android:text="@string/strStream"
                android:textAlignment="center"
                android:textColor="@color/white"
                android:textSize="11sp" />
        </LinearLayout>

        <Button
            android:id="@+id/btnNew"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_marginStart="8dp"
            android:layout_marginTop="8dp"
            android:layout_marginEnd="8dp"
            android:layout_marginBottom="8dp"
            android:layout_weight="0.7"
            android:foreground="@drawable/btm_new"
            android:foregroundGravity="center"
            android:minHeight="48dp"
            tools:ignore="SpeakableTextPresentCheck,TouchTargetSizeCheck" />

    </LinearLayout>

</androidx.constraintlayout.widget.ConstraintLayout>
Copy the code

fragment


      
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/frmArchive"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".fragArchive">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:text="@string/strArchive"
        android:textSize="30sp" />
</FrameLayout>
Copy the code

values

colors


      
<resources>
    <color name="purple_200">#FFBB86FC</color>
</resources>
Copy the code
strings

<resources>
    <string name="app_name">arkk</string>
    <string name="strArchive">ARCHIVE</string>
</resources>
Copy the code

A functional test

Interface switch – Icon & label selected color change

Direction switch – Solve the problem that fragments overlap when direction changes, etc

BottomNavigationView control implementation

The core technology

Fragment

Check out the Fragment series, which is very systematic. Here are the key points related to fragments encountered in this assignment:

Inflate () method

When writing a Fragment Java file, one of the callback methods overridden from the Fragment base class is onCreateView(). If the Fragment has an interface, return the View generated by the corresponding XML file. Null is returned if the Fragment has no interface.

Dynamically Adding fragments

Set up a listener for the click event that triggers the Fragment change

The setOnClickListener() method registers a listener for the control. When clicked, the onClick() method in the listener is executed, distinguishing between different actions by firing the VIEW ID.

Dynamically add Fragment steps
  1. Get FragmentManager in V4 package through getSupportFragmentManager (), native of fragments in the system is through getFragmentManager ().
  2. Start a transaction by calling the beginTransaction() method with FragmentTransaction.
  3. To add a Fragment to a container, use the add() or replace() methods. You need to pass in the id of the container and an instance of the Fragment.
  4. Commit the transaction by calling commit().

A process is similar to a database transaction and must be reacquired each time a FragmentTransaction is used. Each FragmentTransaction can only be committed () once.

FragmentManager

Manage fragments in your activity

FragmentTransaction

Manage the current Fragment

Add (): Adds a fragment to the Activity that has its own view in the Activity container.

Hide (): Hides existing fragments, but only those that have been added to the parent container. Hides the View of the Fragment

Show (): Shows a previously hidden Fragment. This is only relevant to the Fragment that has been added to the Activity. Show the View of the Fragment

Detach (): The Fragment view is destroyed, but its state is not destroyed and is still managed by the FragmentManager.

Attach (): Attach (): Fragment view reloads into UI view and displays it, i.e. onCreateView()→onActivityCreate()→onStart()→onResume()

Replace (): remove(Fragment)→add(int, Fragment, String

The color change that matches the Theme

TextView and imageView use setTextColor() and setColorFilter() respectively, and use getColor(r.color.color_name) to get the values/colors set from res. To match the Theme.

tips

Material Design & bottomNavigationView control

In this assignment, I tried to use two methods to realize Navigation Bar, one is the layout control combination design demonstrated by the teacher in class, and the other is the bottomNavigationView provided by the official.

  • When using the Layout control, the margin parameter refers to the official documentation.

  • The bottomNavigationView control only needs to associate the corresponding menu. XML to automatically generate a Navigation Bar that conforms to the Material Design concept. I’m a big fan of unselected modules showing ICONS and clicking animations. If I have a chance, I might try to use Layout in the future.

Advantages of SVG format

SVG uses XML to define graphics and has many advantages over.jpg,.png, and even.webp:

  • Save time, image has nothing to do with resolution, suitable for different Android models of resolution;
  • Save space, small volume, general complex image can also do KB.

Based on the above advantages, the app ICONS are in SVG format.

The difference between the gravity and layout_gravity properties

  • Gravity: Sets the alignment of its internal elements.
  • Layout_gravity: Sets the alignment of its own container equivalent to the parent container.

Get and getSupport methods

The getSupport method must be used in the V4 package. Otherwise, the application will blink back.

Fragments overlap in situations such as orientation changes

The running Activity is restarted (onDestroy() and onCreate() respectively) when the state changes, such as orientation, screen size, and keyboard visibility. The onSaveInstanceState() method is executed to save some information about the Activity before it is destroyed. This includes fragments that have been added and then restored after a restart, causing subsequent fragments to overlap.

The solution

Add it in androidmanifest.xml

android: configChanges="orientation|screenSize|keyboardHidden

To prevent restarts, let activities not be destroyed or created, and use the original layout by calling onConfigurationChanged() in the onCreate() method.

Repository

Gitee

  • The Gitee control is not compatible with the latest version of AS, so you need to generate an SSN key in git bash and establish a connection to Gitee via repository HTTPS (if rejected using SSN push)

  • If the JetBrains login fails to authorize the GitHub account, press Alt +Insert In the GitHub section of Settings and select Log In with Token