In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-16 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/03 Report--
This article introduces the knowledge of "how to implement and optimize the custom control of panic buying countdown". In the operation of actual cases, many people will encounter such a dilemma. Next, let the editor lead you to learn how to deal with these situations! I hope you can read it carefully and be able to achieve something!
I. Preface
With the continuous development of online shopping, panic shopping countdown has become very common in all kinds of e-commerce applications, this design can improve the click rate and order rate of users.
However, most of the domestic e-commerce applications only support Chinese and are not suitable for other languages, so when the countdown is displayed on the same line as other copywriters, there is no need to consider the countdown display mode. In overseas applications, due to the need to adapt to a variety of languages, the copywriting of some small languages is longer, so when the countdown and other copywriting are displayed on the same line, multilingual adaptation needs to be fully considered. how to gracefully complete the countdown adaptive display is a problem worth pondering.
To further optimize the countdown effect, we added digital scrolling animation to the countdown, as shown in the following figure.
Second, the realization of the basic functions of countdown 2.1 requirements and principle analysis
The control is expected to show two states, an X-day XX:XX:XX before the start of the activity and an X-day XX:XX:XX from the end of the activity, so you need an activity status property and set the copy before the time through the property of whether the activity starts or not. The specific time is independent of each other, so they are split into separate textview for processing.
The core of the countdown control is the timer, and there are already ready-made CountDownTimer classes available in Android to implement the countdown function. In addition, you need to implement some listening interfaces.
2.2 specific implementation 2.2.1 callback listening interface design
First, define the callback interface
Public interface OnCountDownTimerListener {/ * method called while the countdown is in progress * * @ param millisUntilFinished remaining time (Ms) * / void onRemain (long millisUntilFinished); / * * countdown ended * / void onFinish (); / * * method called every minute * / void onArrivalOneMinute ();}
Three methods are defined in this interface:
OnRemain (long millisUntilFinished): a method of countdown to ongoing callback, which is used to expand subsequent functions.
OnFinish (): callback at the end of the countdown, used for switching the active state and pausing the timing, etc.
OnArrivalOneMinute (): callback every minute, which is used as the burial point for regular reporting
2.2.2 Construction and binding of view
Secondly, initialize the custom view. Based on the actual development requirements, the whole control is subdivided into several independent textview such as modified copy, days, hours, minutes, seconds, and initialized in the custom BaseCountDownTimerView:
Private void init () {mDayTextView = findViewById (R.id.days_tv); mHourTextView = findViewById (R.id.hours_tv); mMinTextView = findViewById (R.id.min_tv); mSecondTextView = findViewById (R.id.sec_tv); mHeaderText = findViewById (R.id.header_tv); mDayText = findViewById (R.id.new_arrival_day);} 2.2.3 build private methods for internal use
First, construct a method to set the remaining time. The input parameter is the remaining milliseconds. Inside the method, the time is converted into specific days, minutes and seconds, and the result is assigned to the textview.
Private void setSecond (long millis) {long day = millis / ONE_DAY; long hour = millis / ONE_HOUR-day * 24; long min = millis / ONE_MIN-day * 24 * 60-hour * 60; long sec = millis / ONE_SEC-day * 24 * 60-hour * 60-min * 60; String second = (int) sec + "; / / s String minute = (int) min +" / String hours = (int) hour + ""; / / String days = (int) day + ""; / / Day if (hours.length ()) = = 1) {hours = "0" + hours;} if (minute.length () = = 1) {minute = "0" + minute;} if (second.length () = = 1) {second = "0" + second } if (day = = 0) {mDayTextView.setVisibility (GONE); mDayText.setVisibility (GONE);} else {setDayText (day); mDayTextView.setVisibility (VISIBLE); mDayText.setVisibility (VISIBLE);} mDayTextView.setText (days); if (mFirstSetTimer) {mHourTextView.setInitialNumber (hours); mMinTextView.setInitialNumber (minute); mSecondTextView.setInitialNumber (second) MFirstSetTimer = false;} else {mHourTextView.flipNumber (hours); mMinTextView.flipNumber (minute); mSecondTextView.flipNumber (second);}}
It should be noted that when the unit time is single digits, in order to unify the visual effect, it is necessary to add "0" before the number.
Second, build a method to create a countdown, with the following code:
Private void createCountDownTimer (final int eventStatus) {if (mCountDownTimer! = null) {mCountDownTimer.cancel () } mCountDownTimer = new CountDownTimer (mMillis, 1000) {@ Override public void onTick (long millisUntilFinished) {/ / Planning requirements: when the countdown is 00:00:01, the activity status is refreshed, and the countdown does not show the 00:00:00 status if (millisUntilFinished > = ONE_SEC) {setSecond (millisUntilFinished) / / callback if (eventStatus = = HomeItemViewNewArrival.EVENT_START) {mArrivalOneMinuteFlag--; if (mArrivalOneMinuteFlag = = Constant.ZERO) {mArrivalOneMinuteFlag = Constant.SIXTY; mOnCountDownTimerListener.onArrivalOneMinute () is called every other minute when the active state is in progress } @ Override public void onFinish () {mOnCountDownTimerListener.onFinish ();}};}
In this method, creating a countdown instance CountDownTimer,CountDownTimer () has two parameters, the remaining total time and the refresh interval.
In the onTick () method of the instance, the setSecond () method is called to refresh the view periodically after each interval (that is, 1s) to complete the update of the countdown control. In addition, there is a requirement in the product to report burial sites on an one-minute basis, which can also be done in the onTick () method. In the actual project event, if there are scheduled task requirements, you can also set it freely in this method. Finally, you need to override the onFinish () method of the CountDownTimer to trigger onFinish () in the listener interface.
2.2.4 build a public method for external use
The first is to set the countdown to the listening event:
Public void setDownTimerListener (OnCountDownTimerListener listener) {this.mOnCountDownTimerListener = listener;}
The second is to expose a way to set the initial time and the activity to start or end the copywriting:
Public void setDownTime (long millis) {this.mMillis = millis;} public void setHeaderText (int eventStatus) {if (eventStatus = = HomeItemViewNewArrival.EVENT_NOT_START) {mHeaderText.setText ("Start in");} else {mHeaderText.setText ("Ends in");}}
Finally, and most importantly, the countdown class needs to design ways to start and cancel the countdown:
Public void startDownTimer (int eventStatus) {mArrivalOneMinuteFlag = Constant.SIXTY; mFirstSetTimer = true; / / set the initial value setSecond (mMillis); createCountDownTimer (eventStatus); / / create the countdown mCountDownTimer.start ();} public void cancelDownTimer () {mCountDownTimer.cancel ();}
In the method that starts the countdown, initialize the initial value of the countdown and create the countdown, and finally call the start () method of the CountDownTimer instance to start the countdown. In the canceled method, the cancel () method of the CountDownTimer instance is called directly to cancel the countdown.
2.3 actual calls to the countdown class
When you actually call the countdown control, you only need to add the countdown class layout to the concrete layout and instantiate BaseCountDownTimerView in the called class. Next, initialize the data with setDownTime () and setHeaderText () of the instance, and use setDownTimerListener () to set listening to the view instance.
Finally, call startDownTimer () to start the countdown.
If (view! = null) {view.setDownTime (mDuration); view.setHeaderText (mEventStatus); view.startDownTimer (mEventStatus) View.setDownTimerListener (new BaseCountDownTimerView.OnCountDownTimerListener () {@ Override public void onRemain (long millisUntilFinished) {} @ Override public void onFinish () {view.cancelDownTimer () If (bean.mNewArrivalType = = TYPE_EVENT & & mEventStatus = = EVENT_START) {mEventStatus = EVENT_END; / / is in progress before, and the countdown is changed to 0. If there is another activity / new product, it will be refreshed to the data refreshNewArrivalBeanDate (bean) of the next activity / new product. OnBindView (bean, 1, true, null);} else {setEventStatus (bean);} @ Override public void onArrivalOneMinute () {}}) Third, to achieve the countdown to the overall layout 3.1 requirements description
In multi-language environments or different screen conditions, the length of controls in some languages is too long, so adaptive controls are needed to fold lines to adapt to the UI specification.
3.2 implementation plan
It was originally considered to instantiate only an object with a custom countdown control, but in the process of designing the layout of the object, it is not convenient for an object to be displayed at the beginning of the second line at the end of the line or after the line is folded. Therefore, this paper adopts the method of preset two countdown objects at the same time during layout, one at the end of the line and the other at the beginning of the second line.
In the measure process, if the width of the control is greater than a certain width threshold, initialize the view at the beginning of the line, and set the view visible state at the end of the line to Gone. If it is less than a certain width threshold, initialize the view at the end of the line, and set the view visible state at the beginning of the line to Gone.
First, let's take a look at the xml layout file. The following is an overall layout file main_view_header_new_arrival with the title plus countdown at the end of the line.
Its actual display effect is shown in the following figure
But this layout can only show all the content in a single line, so we need to expand the situation of two-line display on this layout, and take a look at the layout of main_list_item_home_new_arrival.
Its actual display effect is shown in the following figure
Associate the above two view instances in the class respectively.
View.inflate (getContext (), R.layout.main_list_item_home_new_arrival, this); mBaseCountDownTimerViewShort = findViewById (R.id.count_down_timer_short); / / end-of-line countdown viewmBaseCountDownTimerViewLong = findViewById (R.id.count_down_timer_long); / / first-line countdown view
The layout of the countdown controls in both cases has been worked out through the above steps, and then it is time to consider the judgment conditions of the line break display.
In a multilingual environment, the widths of both the title textview and the countdown view are uncertain, so you need to consider the widths of the two controls together. At the same time, because of the planning requirements, it is also necessary to consider the display requirements of the special circumstances of some languages. The judgment code is as follows:
Private boolean isShortCountDownTimerViewShow () {String languageCode = LocaleManager.getInstance (). GetCurrentLanguage (); if (Constant.EN_US.equals (languageCode) | | Constant.EN_GB.equals (languageCode) | | Constant.EN_AU.equals (languageCode)) {/ / due to planning requirements, American English, British English and Australian English are forced to display return true on the right side of the New Arrivals title bar. } else {View newArrivalHeader = inflate (mContext, R.layout.main_view_header_new_arrival, null); TextView newArrivalTextView = newArrivalHeader.findViewById (R.id.new_arrival_txt); LinearLayout countDownTimer = newArrivalHeader.findViewById (R.id.count_down_timer_short); int measureSpecW = MeasureSpec.makeMeasureSpec (0, MeasureSpec.UNSPECIFIED); int measureSpecH = MeasureSpec.makeMeasureSpec (0, MeasureSpec.UNSPECIFIED) NewArrivalTextView.measure (measureSpecW, measureSpecH); countDownTimer.measure (measureSpecW, measureSpecH); VLog.i (TAG, countDownTimer.getMeasuredWidth () + "-" >
In the code, you can customize whether certain languages wrap or not according to the actual needs.
For most remaining languages, you can use MeasureSpec.makeMeasureSpec (0, MeasureSpec.UNSPECIFIED) to get measureSpecW and measureSpecH. The first parameter is the specification value obtained by the system after measuring the View, where 0 is used for omission (the measure method is called directly before the system draws the View, so the width and height is 0, which is independent of the final width and height). The second parameter, MeasureSpec.UNSPECIFIED, represents that the parent container does not have any restrictions on the View. After the acquisition is completed, the measurement of the specific view width will be completed successfully.
Through the return value of this method, we can control the display and hiding of the two countdown view, so as to achieve the effect of adaptive wrapping display.
If (isShortCountDownTimerViewShow ()) {initCountDownTimerView (mBaseCountDownTimerViewShort, bean); mBaseCountDownTimerViewShort.setVisibility (VISIBLE); mBaseCountDownTimerViewLong.setVisibility (GONE);} else {initCountDownTimerView (mBaseCountDownTimerViewLong, bean); mBaseCountDownTimerViewShort.setVisibility (GONE); mBaseCountDownTimerViewLong.setVisibility (VISIBLE);}
In addition, this method is not limited to the countdown control view. For a variety of custom view in multiple languages, this measurement method can still be used to achieve the aesthetic display of adaptive line wrapping.
Fourth, the realization of countdown animation effect 4.1 principle analysis of countdown digital rolling animation
As can be seen from the effect picture, hours, minutes and seconds are all two digits, and the change rules of the numbers are all the same: first, the change of the number starts from the single digit, and the old number moves a certain distance upward from the normal display area. the new number moves a certain distance from the bottom up to the normal display area. If the single digit decreases to 0, the ten digits need to be decremented, so the change is that the ten digits move together with the individual digits.
The specific implementation ideas are as follows:
1. Treat the two digits of hours / minutes / seconds as a digital scrolling component
2. Split the two digits of the digital scrolling component into a digital array, and the change operation can be operated on a single element in the array.
3. Save the old numbers, compare the array elements of the old numbers with the new ones one by one, draw the new numbers with the same bits, and move the different bits together.
4, when moving the number, you need to move the old number up, the distance is 0 to the negative maximum scrolling distance; at the same time, to move the new number up, the moving distance is the maximum scrolling distance to 0; the maximum scrolling distance is the height of the digital scrolling control, which needs to be determined according to the actual UI draft.
4.2 specific implementation of 4.2.1 countdown scrolling component initialization
The countdown scrolling component inherits from TextView and sets [maximum scrolling distance] and [brush related properties] in the constructor, both of which need to be determined based on the actual UI draft.
Among them, the maximum scrolling distance mMaxMoveHeight is the overall height of the hour / minute / second digital control in the UI draft; the font color and size set by the brush are all the font color and size of the hour / minute / second number in the UI draft. The specific code is as follows:
/ / Constructor public NumberFlipView (Context context, @ Nullable AttributeSet attrs) {super (context, attrs); mResources = context.getResources (); / / maximum scroll height 18dp mMaxMoveHeight = mResources.getDimensionPixelSize (R.dimen.qb_px_18); / / set brush related property setPaint ();} / set brush related property private void setPaint () {/ / set the drawing number to white mPaint.setColor (Color.WHITE) / / set the drawing numeral style to solid mPaint.setStyle (Paint.Style.FILL); / / set the drawing numeric font bold mPaint.setFakeBoldText (true); / / set the drawing text size 14dp mPaint.setTextSize (mResources.getDimensionPixelSize (R.dimen.qb_px_14));} 4.2.2 draw countdown scrolling component
Drawing countdown numbers is achieved by overriding onDraw (). First, split the old number and the new number into the corresponding number array
The specific code is as follows:
/ / split the new number into a new number array for (int I = 0; I < mNewNumber.length ()) {mNewNumberArray.add (String.valueOf (mNewNumber.charAt (I);} / / split the old number into the old number array for (int I = 0; I < mOldNumber.length (); iTunes +) {mOldNumberArray.add (String.valueOf (mOldNumber.charAt (I);}
Then draw numbers: when drawing new numbers, judge whether the old numbers and the new numbers are the same bit by bit; if the numbers are the same, draw the new numbers directly; if the numbers are different, both the old numbers and the new numbers need to be moved.
The specific code is as follows:
/ / the text width of a two-digit newNumber int textWidth = mResources.getDimensionPixelSize (R.dimen.qb_px_16); float curTextWidth = 0; for (int I = 0; I < mNewNumberArray.size (); iTunes +) {/ / the boundary mPaint.getTextBounds of each number in newNumber (mNewNumberArray.get (I), 0, mNewNumberArray.get (I). Length (), mTextRect); / / the width of each number in newNumber int numWidth = mResources.getDimensionPixelSize (R.dimen.qb_px_5) / / determine bit by bit whether the old number and the new number are the same if (mNewNumberArray.get (I) .equals (mOldNumberArray.get (I) {/ / the same number, directly draw the new number canvas.drawText (mNewNumberArray.get (I), getWidth () * ONE_HALF-textWidth * ONE_HALF + curTextWidth, getHeight () * ONE_HALF + mTextRect.height () * ONE_HALF, mPaint) } else {/ / numbers are different. Both old and new numbers need to move canvas.drawText (mOldNumberArray.get (I), getWidth () * ONE_HALF-textWidth * ONE_HALF + curTextWidth, mOldNumberMoveHeight + getHeight () * ONE_HALF + mTextRect.height () * ONE_HALF, mPaint) Canvas.drawText (mNewNumberArray.get (I), getWidth () * ONE_HALF-textWidth * ONE_HALF + curTextWidth, mNewNumberMoveHeight + getHeight () * ONE_HALF + mTextRect.height () * ONE_HALF, mPaint);} curTextWidth + = (numWidth + mResources.getDimensionPixelSize (R.dimen.qb_px_3))
GetWidth () gets the entire width of the countdown control; textWidth is the width of two digits; numWidth is the width of a single number; curTextWidth is the spacing of the horizontal starting position of each number, and the spacing between the two digits of curTextWidth=numWidth+.
The starting position of horizontal drawing of ten digits is getWidth () / 2 + textWidth/2;. The starting position of horizontal drawing of single digits is getWidth () / 2textWidth/2 + curTextWidth. GetHight () gets the full height of the countdown control; textRect.height () gets the height of the number.
The vertical drawing starting position of the old number is mOldNumberMoveHeight + getHeight () / 2 + textRect.height () / 2; the vertical drawing starting position of the new number is mNewNumberMoveHeightgetHeight () / 2 + textRect.height () / 2.
4.2.3 realization of countdown digital scrolling effect
The scrolling effect of the old number and the new number is realized by ValueAnimator changing the rolling distance mOldNumberMoveHeight of the old number and the rolling distance mNewNumberMoveHeight of the new number.
Within the specified animation time FLIP_NUMBER_DURATION, mNewNumberMoveHeight needs to change from maximum scrolling distance mMaxMoveHeight to zero mOldNumberMoveHeight needs to change from 0 to negative maximum scrolling distance mMaxMoveHeight; each time after calculating a new scrolling distance, call the invalidate () method, trigger the onDraw () method, and constantly draw old numbers and new numbers to achieve the effect of digital scrolling.
The specific code is as follows:
/ * use ValueAnimator to change the value from MAX_MOVE_HEIGHT to 0 within the specified time FLIP_NUMBER_DURATION, and each value change is assigned to mNewNumberMoveHeight. At the same time, the value of mNewNumberMoveHeight-MAX_MOVE_HEIGHT is assigned to mOldNumberMoveHeight and redrawn to realize the upward sliding of new numbers and old numbers. * / mNumberAnimator = ValueAnimator.ofFloat (mMaxMoveHeight, 0); mNumberAnimator.addUpdateListener (new ValueAnimator.AnimatorUpdateListener () {@ Override public void onAnimationUpdate (ValueAnimator animation) {mNewNumberMoveHeight = (float) animation.getAnimatedValue (); mOldNumberMoveHeight = mNewNumberMoveHeight-mMaxMoveHeight; invalidate ();}}); mNumberAnimator.setDuration (FLIP_NUMBER_DURATION); mNumberAnimator.start (); 4.3 specific use
First introduced in the layout, the usage is the same as TextView. The following figure shows the layout corresponding to hours, minutes and seconds:
Then find the corresponding countdown numeric control through id:
MHourTextView = findViewById (R.id.hours_tv); mMinTextView = findViewById (R.id.min_tv); mSecondTextView = findViewById (R.id.sec_tv)
Finally, the method of the hour / minute / second countdown digital control is called to set the initial countdown value or the new countdown number. If this is the first countdown, you need to call the setInitialNumber () method to set the initial value; otherwise, call the flipNumber () method to set the new countdown value.
The specific usage is as follows:
If (mFirstSetTimer) {mHourTextView.setInitialNumber (hours); mMinTextView.setInitialNumber (minute); mSecondTextView.setInitialNumber (second); mFirstSetTimer = false;} else {mHourTextView.flipNumber (hours); mMinTextView.flipNumber (minute); mSecondTextView.flipNumber (second); 5. Principle analysis of optimizing countdown performance 5.1 countdown digital scrolling animation
In the implementation, the countdown control is a child element of ListView, and the ListView is in a Fragment.
To reduce power consumption, you need to pause the countdown when the countdown control is not in the visible range, and restart the countdown when the countdown control reappears in the visible range. The following picture shows the countdown pause and start.
5.2 specific implementation of 5.2.1 pause countdown
The page slides and the countdown control slides out of the visual area. When the countdown control slides out of the visual range of the ListView, the countdown needs to be paused. The point of this case is that you need to determine whether the child view has been moved out of the ListView.
If the application only needs to be compatible with Android 7 or above, you can cancel the countdown in the method body by overriding the onDetachedFromWindow () method. Because this method is called every time the child view moves out of the ListView.
@ Overrideprotected void onDetachedFromWindow () {super.onDetachedFromWindow (); / / remove the screen call, pause the countdown stopCountDownTimerAndAnimation ();}
If the application needs to be compatible with Android 7 or less, the above method will fail because the onDetachedFromWindow () method is not compatible with earlier versions. But the same effect can be achieved by overriding the onStartTemporaryDetach () method.
@ Overridepublic void onStartTemporaryDetach () {super.onStartTemporaryDetach (); / / remove the screen call, pause the countdown stopCountDownTimerAndAnimation ();}
Switch to another Fragment via tab
When the countdown control is in visual range, you need to pause the countdown when you switch to another Fragment via tab. In this case, the Fragment of the countdown control is hidden, and you can get the View of the countdown control when the Fragment is hidden, and then call its method to pause the countdown.
@ Overridepublic void onFragmentHide () {super.onFragmentHide (); / / pause the countdown stopNewArrivalCountDownTimerAndAnimation ();}
In order to get the View object in which the countdown control is located, we determine whether it is the View object in which the countdown control is located by traversing the child View in the visual range of the ListView. Then call the stopCountDownTimerAndAnimation () method of the View object where the countdown control is located to pause the countdown.
/ * get the view object where the countdown control is located, pause the countdown * / private void stopNewArrivalCountDownTimerAndAnimation () {if (mListView! = null) {for (int index = 0; index < mListView.getChildCount (); index++) {View view = mListView.getChildAt (index); if (view instanceof HomeItemViewNewArrival) {((HomeItemViewNewArrival) view) .stopCountDownTimerAndAnimation () }
Application switches to background / jumps to other interfaces
When the countdown control is in the visual range, the countdown needs to be paused when the application switches to the background or clicks on other contents of the interface where the countdown control is located, and jumps to another interface. Because of these conditions, the onStop () method of the Fragment where the countdown is located will be triggered. So you can override onStop (), get the View of the countdown control inside the method body, and then pause the countdown.
The stopNewArrivalCountDownTimerAndAnimation () method is the same as above.
@ Overridepublic void onStop () {super.onStop (); / / pause the countdown stopNewArrivalCountDownTimerAndAnimation ();} 5.2.2 start the countdown
The page slides, and the countdown control slides into the visual area
When the countdown control slides out of the visual area and into the visual area again, the getView () method of Adapter is automatically called, and then the onBindView () method of the countdown control is called. Because the countdown control is initialized in the onBindView () method, there is no need to start the countdown manually in this case.
Switch back to the Fragment where the countdown is based via tab
Switch back to the Fragment where the countdown control is located through tab. If the countdown control is in visual range, you need to start the countdown again. Because the Fragment is redisplayed in this case, you can get the View of the countdown control when the Fragment is displayed, and then call its method to restart the countdown.
@ Overridepublic void onFragmentShow (int source, int floor) {super.onFragmentShow (source, floor); / / restart the countdown refreshNewArrival ();}
Similarly, in order to get the View object in which the countdown control is located, you need to determine whether it is the View object in which the countdown control is located by traversing the child View within the visual range of the ListView. Then call the refreshEventStatus () method of the View object where the countdown control is located to begin the countdown.
/ * get the view object where the countdown control is located, and start the countdown * / private void refreshNewArrival () {if (mListView! = null) {for (int index = 0; index < mListView.getChildCount (); index++) {View view = mListView.getChildAt (index); if (view instanceof HomeItemViewNewArrival) {((HomeItemViewNewArrival) view) .refreshEventStatus () }
The application switches back to the foreground / rollback from other interfaces
When the application switches back to the foreground or from another interface to the interface where the countdown control is located, if the countdown control is in visual range, you need to restart the countdown. Because of these conditions, the onResume () method of the Fragment where the countdown is located will be triggered. So you can override onResume (), get the View of the countdown control in the body of the method, and then call its method to restart the countdown.
Where the refreshNewArrival () method is the same as above.
@ Overridepublic void onResume () {super.onResume (); / / restart the countdown refreshNewArrival ();} "how to implement and optimize the custom control of panic buying countdown" ends here. Thank you for reading. If you want to know more about the industry, you can follow the website, the editor will output more high-quality practical articles for you!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.