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
- Get FragmentManager in V4 package through getSupportFragmentManager (), native of fragments in the system is through getFragmentManager ().
- Start a transaction by calling the beginTransaction() method with FragmentTransaction.
- 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.
- 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