0x0, long lost read


  • Ashamed => It has been three months since the last article was published. It is not all because of laziness, but because I am caught up in trifles;
  • Originally => thinking about two months brush brush problem, while the gold nine silver ten empty when another job;
  • Results => Time was spent on the company’s new APP, spicy chicken products and background mixed doubles, scalp numbness;
  • I worked late into the night for N times, and doubted my life for many times.
  • Despite => being swamped with ZZ projects and talent and not learning anything new;
  • => < p style = “margin-bottom: 0pt; margin-bottom: 0pt;
  • See => the last article “forget him! I steal other people’s APP code to raise you” good response;
  • => Everyone was very interested in stealing the code and made fun of the “nugget message card code”.
  • If you decide to go on stealing code, “steal UI effects first” and then “reverse correlation basics”;
  • => APP “XX English”;
  • Why => Of course, it is not out of nowhere, listen to me ~ ~

For the record:

  • 1. The author is just curious about the technology and does not damage the APP maliciously;
  • 2. Only for technical learning, respect the original developer’s labor results, not for commercial purposes;

0x1. Directly bring the competing product you want to copy


Remember that on the third day after the “so-called requirements review”, the designer threw a piece of paper with a page like this:

And he came over and he said to me,

This page shows all the lessons, and then you can slide, and the background has to move as you slide…

Listen to me:??

The short but pithy catchphrase came out:

Just bring the competing APP you borrowed

Then the designer opened the rival APP “XX English” and showed it to me:

Yo~ game level learning APP, remember a long time ago in an English APP also saw this page, but I used Cocos2d, if this is like this, I can not do it, first to identify “page is not written native”.


How can you tell if a page is written native


The identification method is very simple, the phone in turn:

Open Developer Tools -> Select Show Layout Boundaries

If a border and line like this appears:

It is written native, otherwise it may be Cocos2d, web pages or custom controls, etc. Since it’s primitive, it means there’s a chance, but it may take a while to get into the habit of “putting on an embarrassed look.”

After persuading the designer with uncertain words such as “should, maybe, may”, I began to play with this competing APP. The first feeling was “exquisite”, and I beat our APP with “design, dynamic effect, original painting and content”. I don’t know where our product brother got confidence and wanted to make money:

I just wanted to see how this page worked, but it got out of hand:

It was like:

  • There is a long acquaintance of the old driver, said to take you to a big health care, increase knowledge;
  • How about you: I have already heard of big Health care, but I dare not go there without anyone to guide me.
  • Because of the face, you still accept the invitation, ordered the cheapest foot washing, thought to wash the feet, then leave;
  • The manager expertly shows you to a somewhat dark room and tells you to wait. The technician will be with you soon.
  • After the manager leaves, you look like a curious child and search the room, but you can’t find any dishes for washing your feet.
  • A short knock on the door asked, “May I come in, Sir?” Sweet voice frighten you quickly sit back;
  • “Come in”, a woman with a nice figure pushed the door and entered, “pretty boy, long waiting, sorry, tonight too many people”;
  • The dim lighting and thick makeup on the face of the technician make it difficult for you to see her appearance, and your intuition tells you that she is probably between 25 and 35 years old.
  • You also do not keep staring at the technician’s face, after all, it is not polite, I do not know what to say for a moment, the atmosphere slightly embarrassed;
  • You hold in a sentence: “that, I wasn’t the point wash feet, how didn’t see wash feet dish?”
  • The technician smiled and said, “Oh, the technician who washed the feet is on the clock. I think it will take two hours.”
  • You have some dissatisfaction: “that how to do, my money all gave, technician insufficient, the manager also did not say with me!” ;
  • The technician takes apology slightly: “pretty boy, really sorry, want to change to push back for you?”
  • You: “Push back? Is it the same price? For what?”
  • Technician: “be push push back, massage massage acupoint, promote blood circulation, add 100 yuan.”
  • You: “Wow, expensive so much, I wash a foot is only 45, forget it, don’t press”, and then ready to wear shoes to leave;
  • The technician holds your arm: “pretty boy, your friend ordered this, you go out to wait also want to wait 45 minutes, rare come once, try!” ;
  • Your second thought: also right, go out to wait for boring do not say, the old driver came out to see me sitting, much have no face.
  • It’s 100, it’s 100, it’s just one time (however, it’s the same as women’s clothes, it only has zero and unlimited times)
  • “Ok, add 100 massage,” technician a listen, can not help but smile, you have to see some distraction;
  • After a little bashful conversation with the mechanic, the manager knocked at the door and brought in a small basket;
  • You glance at the contents of the basket: several small POTS, transparent cloth like mosquito nets, and two pieces of jelly;
  • I can understand the cloth. It’s probably for wiping. What are these two jellies? Snacks? But isn’t that cheap?
  • Technician say: “beautiful boy, milk or essential oil open back”, pull your divergent thoughts back;
  • “Milk bar,” according to the technician, took off his shirt, lay down, and began to push back, a really good gesture,
  • Press y you are a burst of soft, coupled with the technicians of a touted to you, can not help but some high;
  • 45 minutes to the blink of an eye, the door bell rang to announce the end of the massage;
  • You have some more desire, the technician seems to see through your mind, “pretty boy, comfortable, do you want to add a clock?”
  • You: “HMMM, pretty comfortable, add the words of the clock how much money, or push back?”
  • The technician suddenly blushes slightly, “It is also 100, or push, is the way and part of the push a little different…”
  • You seem to get the √ of what, “Yo? That’s interesting. All right, add a hundred. I’ll see how you do it.”
  • Technician: “Well”, and then he took out the cloth and two jellies in the small basket.
  • Now you know:
  • It was not a piece of cloth, but a very transparent garment;
  • And two jelly are not snacks, but “crystal love” props;
  • … A Mosaic of sex, To be continued…

The above story is pure fiction!! The author is also from others there listen to come back, have not been to this kind of place!!

Just to say that it’s fun to “rip the code”, from “one UI effect” to “all UI effects” to “data” and “architecture” to “use it for my own use”. Like you from the beginning just want to “wash feet” to “crystal love”, “around”, “ice and fire” and so on. However, it is recommended to see more “excellent open source projects”, after all, “roadside wildflowers” (stealing code), attract you is not incense, but wild. I have no culture and can only find such vulgar examples to express my feelings. Please forgive me

Okay, that’s a little too much nonsense. Move on!

Right, after the event from the old driver that: there is no mechanic washing feet…


I want to see what medicine you sell in this X


Some useful information from the developer assistant:

  • 1. Application package name: com.Knowbox.en
  • 2. The current page name is MainActivity
  • 3. Run the MapFragment command to view the Fragment

Next, type the following ADB command to get information about the Activity at the top of the current stack:

adb shell dumpsys activity top > info.txt

Copy the code

Open the info.txt output file, go to MainActivity, and look at the layout hierarchy:

BaseUIRootLayout, MapViewPager, 5 RecyclerView in view, EM… The implementation principle should not be: “sliding offset dislocation”

That is, when one list slides, the other lists slide by different distances. For example, if the list slides by 10, the other lists slide by 102,103, 10*4, respectively

Guess that, next decompile validation wave, no hardening, directly execute the decompile batch script (write your own) :

Waiting for decompilation to complete:

Next, Android Studio imports the decompiled Jadx directory (the apktool directory is for smail code) :

Search the MapFragment file globally and then r.Layout. Find the layout file name:

Then search the layout file globally: layout_main_map

Em… The layout is the same as our ADB Dumpsys content, five RecyclerView, and then open the MapFragment to start with the code, but the beginning of the OnScrollListener gives the answer:

In this case, bcDE is the obfuscated variable name. Scroll down to see:

2131690465 is the control ID. Search globally and you can find the corresponding value in the R file

Find the corresponding ID, here directly replace:

The scroll listener list is set to the following:

Ok, slide offset dislocation, oh, I suddenly think of a problem, several lists can slide, how to use this list as the standard:


OnTouch () returns true, making the onTouchEvent method of Recyclerview uncalled (thus blocking swiping and clicking).

Ok, I got it. Start moving


0x4, steal: ① Slide offset dislocation effect


1. Layout of list content

The brainless handling layout is simply wrapped around ConstraintLayout:


       

<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="match_parent">




    <androidx.recyclerview.widget.RecyclerView

            android:id="@+id/rv_back_level_bg"

            android:layout_width="0dp"

            android:layout_height="0dp"

            app:layout_constraintStart_toStartOf="parent"

            app:layout_constraintEnd_toEndOf="parent"

            app:layout_constraintTop_toTopOf="parent"

            app:layout_constraintBottom_toBottomOf="parent"/>




    <androidx.recyclerview.widget.RecyclerView

            android:id="@+id/rv_middle_level_bg"

            android:layout_width="0dp"

            android:layout_height="0dp"

            app:layout_constraintStart_toStartOf="parent"

            app:layout_constraintEnd_toEndOf="parent"

            app:layout_constraintTop_toTopOf="parent"

            app:layout_constraintBottom_toBottomOf="parent"/>




    <androidx.recyclerview.widget.RecyclerView

            android:id="@+id/rv_front_level_bg"

            android:layout_width="0dp"

            android:layout_height="0dp"

            app:layout_constraintStart_toStartOf="parent"

            app:layout_constraintEnd_toEndOf="parent"

            app:layout_constraintTop_toTopOf="parent"

            app:layout_constraintBottom_toBottomOf="parent"/>




    <androidx.recyclerview.widget.RecyclerView

            android:id="@+id/rv_line"

            android:layout_width="0dp"

            android:layout_height="80dp"

            android:paddingStart="135dp"

            android:paddingEnd="80dp"

            app:layout_constraintStart_toStartOf="@id/rv_main_homework"

            app:layout_constraintEnd_toEndOf="parent"

            app:layout_constraintTop_toTopOf="parent"

            app:layout_constraintBottom_toBottomOf="parent"

            android:clipToPadding="false"/>




    <androidx.recyclerview.widget.RecyclerView

            android:id="@+id/rv_main_homework"

            android:layout_width="0dp"

            android:layout_height="wrap_content"

            android:paddingStart="55dp"

            android:paddingEnd="0dp"

            app:layout_constraintStart_toStartOf="parent"

            app:layout_constraintEnd_toEndOf="parent"

            app:layout_constraintTop_toTopOf="parent"

            app:layout_constraintBottom_toBottomOf="parent"

            android:clipToPadding="false"/>




</androidx.constraintlayout.widget.ConstraintLayout>

Copy the code

Tips

ClipToPadding =”false” RecyclerView: recypTopAdding =”false” RecyclerView: recypTopAdding =”false” RecyclerView: RecypTopAdding =”false”

   


 


In addition, you need to add another attribute:
clipChildrenThis property is:
Sets whether the child view can override the parent view!!!!!!!!!


2, front and back background picture Adapter


There is one Adapter for the three background images. I did not find the corresponding image files in the Res and Assets directories. I guessed that the materials were downloaded online, but suddenly I remembered that I had downloaded the resources when I entered the APP at the beginning. Clean up the data, open Fiddler, grab the packet, and open the APP:

Open the browser directly, download the file to the local decompress:

Em… Look at the file name, it is not difficult to find three types of images, front, middle and back, open pictures in turn:

Drawable -xxhdpi drawable-xxhdpi drawable-xxhdpi drawable-xxhdpi drawable- xxHDpi drawable- xxHDpi Add bg_ prefix at the beginning of the index, instead of manually changing one by one, write a batch rename script.

import os



pic_source_dir = os.path.join(os.getcwd(), "lisk5"+os.sep) # Original image path



if __name__ == '__main__':

    file_list = []

    f = os.listdir(pic_source_dir)

    for i in f:

        if i.endswith(".png") :

            os.rename(os.path.join(pic_source_dir, i),

                      os.path.join(pic_source_dir, "bg_%s" % i))

    print("Batch finished!")

Copy the code

Before writing the Adapter, write the layout of each Item. The layout of each Item is ImageView. The layout of each Item is ImageView.


       

<androidx.constraintlayout.widget.ConstraintLayout 

    xmlns:android="http://schemas.android.com/apk/res/android"

    android:layout_width="wrap_content"

    android:layout_height="match_parent"

    xmlns:app="http://schemas.android.com/apk/res-auto">




    <androidx.appcompat.widget.AppCompatImageView

        android:layout_width="wrap_content"

        android:layout_height="0dp"

        android:adjustViewBounds="true"

        app:layout_constraintStart_toStartOf="parent"

        app:layout_constraintEnd_toEndOf="parent"

        app:layout_constraintTop_toTopOf="parent"

        app:layout_constraintBottom_toBottomOf="parent"

        android:src="@drawable/bg_1_back_01" />




</androidx.constraintlayout.widget.ConstraintLayout>

Copy the code

However, those of you who have studied performance optimization know that “you should minimize unnecessary nesting of layout layers.” To play this way, you need to stack three layers of ConstraintLayout… Actually, you can dynamically add an ImageView, but you just need to determine its exact width and height. Ok, so let’s see how the APP does this, go to the file MainHomeworkBgAdapter, go to setLayoutParams:

There’s a lot of A’s here. Let’s look at them one by one.

Well, this is defining an ImageView, then going to the onBindViewHolder with two a(((XXX) this.c.xx:

Function a returns the width of the image from the Bitmap and sets the image from the ImageView. Then we go to this.a, the outermost a, which stores the width of a temporary variable. This piece of code is interesting:

We know from the unpacked bundle that all the images except for the last item are 500 width and the server internal error code is 500~

This temporary variable is initialized in the constructor:

Locate the b function of UIUtils:

Em, is to get the height of the screen, from here the whole process is clear, start writing Adapter


3, oblique dotted Adapter


MapFragment. Kt: MapFragment. Kt: MapFragment.

If you enter the LineAdapter, you will see a LineView in the ViewHolder.

Enter the LineView with the following code:

Briefly describe the process:

  • Constructor: setWillNotDraw(false), which is needed to override ViewGroup (if I remember correctly). Set false to make ViewGroup onDraw().
  • 2, method a: initialize the Paint brush and Path Path;
  • 3. OnLayout method: Obtain width and height;
  • 4, onDraw method: set the start and end Y coordinates according to up or down, and then draw the line
  • 5, setIsUp method: set the drawing direction is up or down.

Also carry a wave of code:

And then we go back to the LinearAdapter, which is a little bit simpler, and here’s the core:

SetVisibility where 0 and 4 are “VISIBLE” and “INVISIBLE” respectively, followed by the circled judgment conditions: the head and the end of the dotted line does not show understandable, is this. Just move the code and see what happens without judging:

After the operation:

Oh, my God, there’s one missing, so what’s this. A for? You can see that the constructor is passed a Z, following:

Z with an initial value of false determines whether a wave of this.j.is equal to 1, and if it is true, what is this.j.is? Here we do not follow, directly use “Smail dynamic debugging” this APP, “how to prepare for foreplay, next section will teach you”, here assuming foreplay is done, start to teach ~ find the approximate position of the break point:

Terminal command execution script:

Waiting for Debugger OK, debug, select APP and click OK

When you reach the breakpoint, the program will automatically hang and the Android Debugger window will pop up.

You can see that the Adapter is passed as arguments 50 and true, followed by this.j.but it is a string: “1-49”. What the hell?

if(String == 1)

Copy the code

This. J: This. J: this.

Locate the OnlineMainCourseIndexInfo class:

Parse can see from the parse that the “I” should be the current map ID, but instead it is “1-49”. This is more like j’s current map level, and G is more like openCartoonVideo. So in fact, the corresponding parameter is H, that is, 1, which represents the level, so directly ignore, the modified code is as follows:

After the operation:

Ok, that’s what we want, and we’re left with the Adapter.


4. Adapter in front


Directly locate the MainHomeworkAdapter:

Tut, RecyclerView multi-item layout, see name, table head table tail, and the middle, handling write Adapter prototype (here is not to write click the event)

The data class has two variables that we don’t know what they do:

The CommonAdapter is a CommonAdapter. The CommonAdapter is a CommonAdapter. The CommonAdapter is a CommonAdapter.

StartHolder and EndHolder are simpler:

Adapter is written, and then is to create data, still under the breakpoint debugging,

Copy and paste, and cycle to create some fake data:

After tinkering, here’s how it works:

All right, so the theft is done


0x5, steal: ② Font TextView


We all know that you can call TextView setTypeface to set fonts, if an APP uses more than one font package, every time to set it seems a little cumbersome, this APP directly rewrite TextView, direct XML reference, much more convenient, the author on the basis of the original to do a little change, there is a default font, You can set the font separately in the XML, and add one attribute to attrs. XML:

EnTextView inherits from TextView, gets the properties, and sets the font:

Then set the attributes in XML:


0x6, Steal: ③ Airbnb Lottie

In fact, most of the prettier looking animations in the competition come from Aribnb’s Lottie library, like this one (floating elephant that blinks) :

There is also the animation of white circle spreading ripple, if you were to do it, what would you do?

  • 1, frame animation: need to add a lot of pictures (size adaptation), will inevitably lead to APK volume skyrocketing;
  • 2. Gif: Gif picture occupies a large space, and needs to adapt to a variety of screens, the impact is the same as above;
  • 3. Attribute animation + Image + SVG: complicated and not easy to maintain, a little change may have to start all over again;

Using the Lottie library allows us to develop animations without getting tangled up in complicated effects. There is a lot of information about it on the Internet.

Use After Effects and Bodymovin to export the animation to a JSON file. Step 3: Build. Gradle Import Lottie – Android library, and introduce LottieAnimationView into the XML for direct use.

More instructions are available:

  • Liverpoolfc.tv: github.com/airbnb/lott…
  • Or, from the perspective of Android development, Airbnb’s Lottie

Handling:

Then fill in the right side of the display animation, click after scrolling will start position

The running effect is as follows:


0 x7, summary


Although in front of the FLAG said to “rip all UI effects”, but only a demonstration, after all, the purpose of writing articles is to show techniques, let the reader draw lessons from one example, and rip other people’s source code is not a simple thing. A bunch of obfuscated ABcds, then various inherited parent classes nested, coupled, a bunch of unused code, to extract a single control, very time consuming. Or that sentence, design or product to copy when go to pick, will be some practical, with the purpose to see the source code! UI related to the first to stop here, the next section to explain a wave, the author of the APP used by others all the “basic reverse operation” thank you ~

Rightness, mix the gold that excavate also quite long, after following white piao notebook, a few days ago white piao again a mouse mat, Thanksgiving! Send “a self-written Python Crawler” in the comments section, free shipping, next Friday


Source address: github.com/coder-pig/S…


References:

  • Android: Lottie– Makes Android animation more elegant
  • Lottie – Makes complex animations so simple

Congratulations “ALuoBo” winning “to draw on screen in: www.bilibili.com/video/av757…